home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / AMDoc_0_9.lha / AMDoc / Scenario.txt < prev    next >
Text File  |  1995-01-21  |  152KB  |  2,870 lines

  1. AmigaMUD, Copyright 1995 by Chris Gray
  2.  
  3.  
  4.             The Standard Scenario
  5.  
  6.  
  7. This file is intended as commentary and explanation of the standard
  8. scenario sources shipped with AmigaMUD. It cannot explain everything
  9. in those source files, else it would be a very long boring book. My
  10. hope is to explain enough of what is done in those files that people
  11. interested in scenario programming in AmigaMUD can learn from those
  12. examples, and, if they choose, can program within the framework of the
  13. standard scenario. The level of detail in these descriptions will
  14. decrease throughout this file. It is assumed that the reader will
  15. start at the beginning and read forward, and will understand the early
  16. material enough to be able to follow similar things without detailed
  17. explanations.
  18.  
  19. The structure of the files is such that parts of the scenario can
  20. easily be left out, and other new parts can, hopefully, be inserted.
  21. This is done by having two levels of file inclusion, using the wizard-
  22. mode command "source". The file names are referenced relative to the
  23. assigned name "ST:", so, in order to create the standard scenario from
  24. these source files, you will have to have an assign of that name
  25. pointing to the directory you have the source files in. Note that the
  26. scenario source files no longer fit on a standard Amiga floppy disk,
  27. so people with no hard drive or high-density floppy drive will have to
  28. trim the scenario down, or build it in multiple pieces. I will not
  29. discuss these difficulties further, and will just assume that the
  30. reader will be able to make things work out.
  31.  
  32. To create the standard scenario from the source files, you must:
  33.  
  34.     - make sure the source files are ready, and assign "ST:" is set
  35.  
  36.     - choose a directory (I often use the RAM-disk) which the database
  37.     files are to be created in, and CD to that directory
  38.  
  39.     - run MUDCre to create the initial database
  40.  
  41.     - run MUDServ in the background, using "Run". I usually specify a
  42.     cache size of 500000.
  43.  
  44.     - enter    [path/]smud <st:go
  45.  
  46. After a few minutes of work, depending on the speed of your CPU and
  47. disk, the scenario should be built. If you have modified the scenario
  48. source files and made errors which the AmigaMUD interpreter detects at
  49. this point, then you will see error messages coming out. If the number
  50. of messages is large, and threatens to scroll out of your window, you
  51. can hit CNTL-C to abort the run. Then, shut down MUDServ (using
  52. MUDShut or the CLI BREAK command), go fix your errors and try again
  53. from the "run MUDCre" step.
  54.  
  55.  
  56. Overall Organization
  57.  
  58.  
  59. Let's look at the initial file, "st:go" in detail:
  60.  
  61.  1 SysAdmin
  62.  2 SysAdminPassword
  63.  3 ignore RunLimit(100).
  64.  4 use Characters
  65.  5 public G CreateGrammar().
  66.  6 SetContinue(true).
  67.  7
  68.  8
  69.  9 /* the basics of this scenario */
  70. 10 source st:basics.m
  71. 11
  72. 12 /* Combat code - player v.s. machine and player v.s. player. */
  73. 13 source st:combat.m
  74. 14
  75. 15 /* The town */
  76. 16 source st:town.m
  77. 17
  78. 18 /* The Proving Grounds area - 3 quests, weapons, special monsters, etc. */
  79. 19 source st:proving.m
  80. 20
  81. 21 /* The on-line building commands and interactive stuff. Builder's guild. */
  82. 22 source st:build.m
  83. 23
  84. 24 /* newsroom, telegram office - usenet news and email reading/posting. */
  85. 25 source st:usenet.m
  86. 26
  87. 27 SetContinue(false).
  88. 28 NewCreationPassword().
  89. 29
  90. 30
  91. 31
  92. 32 Print("Scenario parsed - flushing database.\n").
  93. 33 Flush().
  94. 34 ignore RunLimit(10).
  95. 35 Print("All done.\n").
  96.  
  97. When SMUD is run, it does a normal session with AmigaMUD. It first
  98. prompts for a character name, then for a password. After that it is
  99. ready for input from the player. In the initial database, the only
  100. character is SysAdmin, with password "SysAdminPassword". SysAdmin is
  101. in wizard-mode, since there is no scenario yet to handle non wizard-
  102. mode input. When its input is coming from a file, SMUD does not put
  103. out prompts, so you will not see them when running it with its input
  104. redirected from a file.
  105.  
  106. Lines 1 and 2 simply log SysAdmin into AmigaMUD, and leave him in
  107. wizard-mode, ready for commands. Line 3 is a simple call to the
  108. builtin function "RunLimit". This builtin sets the time limit for the
  109. processing of each line of input, button click, etc. Since some of the
  110. things done in creating the scenario can take a while on a slow CPU,
  111. we want a fairly large limit (100 seconds), so that we don't get
  112. unwanted failures. The time limit is put back to a more reasonable 10
  113. seconds at the end of building the scenario, on line 34.
  114.  
  115. Line 4 adds the Characters table (which contains the names of all
  116. characters in the scenario) to the set of in-use tables. The set of
  117. tables is initially just the table of builtin functions, the public
  118. table and SysAdmin's private table. Line 5 creates the main grammar
  119. used in the scenario for parsing user input. The short name for the
  120. symbol, "G", was picked because it is used a lot in many places in the
  121. scenario sources. Note that G is public, so that others can reference
  122. it by name. Line 6 calls the "SetContinue" builtin, passing "true" as
  123. the parameter. This tells the system that functions that have internal
  124. errors during compilation should still be entered into whatever symbol
  125. table they are intended for. In normal circumstances, such erroneous
  126. symbols are not defined. Since we are sourcing many files non-
  127. interactively, however, we want the symbols defined so that calls to
  128. them from other functions we are defining are not flagged as errors.
  129. Doing this avoids a lot of spurious error comments. We set back to the
  130. normal state after creating the scenario, on line 27.
  131.  
  132. Line 10 is the first real step in creating the scenario. It sources
  133. file "st:basics.m" (I use suffix ".m" for AmigaMUD source files - my
  134. apologies if that gets confused with a suffix for, say, Modula source
  135. files). That file in turn, sources a number of other source files,
  136. which I will get to later. It is the only file that is needed if you
  137. want to just work within the framework of the standard scenario,
  138. without actually using any of the rooms or quests of the scenario. You
  139. will also want file "st:combat.m" (line 13) if you want to have the
  140. standard scenario's version of combat.
  141.  
  142. Line 16, bringing in file "town.m", creates the mini-mall area; the
  143. streets of the town; the squirrel quest; the non-player characters
  144. Packrat and CareTaker; and the in-MUD mail and bulletin-board
  145. facility, including Postman. These areas include the "pear" quest.
  146.  
  147. Line 19, bringing in file "proving.m", creates the entire "Proving
  148. Grounds" area, which is the combat area in the scenario. This is the
  149. largest area in the scenario, and includes 3 quests. Note that this
  150. area has a minor dependency on the town stuff, which is its point of
  151. attachment.
  152.  
  153. Line 25, bringing in file "usenet.m", creates the simple electronic
  154. mail and usenet news facilities. They are dependent on the town area
  155. also, for their locations (the Telegram Office and the News Room).
  156. These facilities are not included in the shipped scenario database, so
  157. as to not cause problems for those not running the required supporting
  158. software.
  159.  
  160. Lines 28, 29 and 30 change the "player creation password" to an empty
  161. string, which means that no password is required to create a new
  162. character. If you want a password to be required, you can change lines
  163. 29 and 30 to the chosen password, or change it online by calling
  164. "NewCreationPassword" as SysAdmin in wizard mode. To disallow creation
  165. of characters by users (in which case SysAdmin must explicitly create
  166. each character using "CreateCharacter"), set the player creation
  167. password to be an asterisk.
  168.  
  169. Line 33 uses the "Flush" builtin to cause the server to flush all of
  170. its caches out to the database files. This can take a while. Partial
  171. flushes will have been done automatically by the system earlier,
  172. unless you used a very large database cache.
  173.  
  174. When the message "All done." appears, the standard scenario has been
  175. recreated, and SMUD, reaching the end of file "st:go", will exit. If
  176. you wish to run from the database created, I suggest you shut down
  177. MUDServ at this point and make a backup of the database files.
  178.  
  179.  
  180. Tables Used
  181.  
  182.  
  183. There are many symbols created in the standard scenario. When trying
  184. to work within it, especially interactively, it can be difficult to
  185. find a table containing a symbol that is referenced by a function you
  186. are displaying. For reference, a complete list of the tables created
  187. and used in the scenario is given here. All table names start with
  188. either "t_" or "tp_". The former are public tables, available for use
  189. by all programmers. The latter are private to SysAdmin, and are not
  190. needed by other programmers. Such private tables contain the many
  191. symbols used for the rooms in parts of the scenario, local functions
  192. used only in certain areas, the functions used with verbs, etc. In the
  193. following list, the name of the table is given, followed by the source
  194. file it is defined in, and a description of what is in it.
  195.  
  196.     tp_misc    basics.m    symbols that are used in several
  197.                 source files, but that are not made
  198.                 public.
  199.  
  200.     t_base    basics.m    public symbols which are basic to the
  201.                 entire scenario. This includes many
  202.                 properties on characters, rooms, and
  203.                 objects, as well as routines which are
  204.                 basic to the scenario.
  205.  
  206.     t_icons    basics.m    public symbols relating to icons.
  207.  
  208.     t_graphics    basics.m    public symbols relating to the various
  209.                 styles of graphics supported by the
  210.                 scenario. Includes the exported auto-
  211.                 graphics routines, names for the
  212.                 standard colours, the effects-id
  213.                 generator, etc.
  214.  
  215.     t_util    basics.m    public symbols, mostly functions,
  216.                 which provide utilities useful for
  217.                 constructing and operating the
  218.                 scenario. For example, the code for
  219.                 stores and banks is here.
  220.  
  221.     t_roomtypes basics.m    small table containing the most-
  222.                 general models for rooms. These should
  223.                 be inherited by all rooms in the
  224.                 scenario, so that code which depends
  225.                 on things like indoors-versus-outdoors
  226.                 can work correctly.
  227.  
  228.     t_quests    basics.m    small table exporting the functions
  229.                 needed to add a quest to the scenario.
  230.                 Also has the stuff required to setup
  231.                 Questor's Office.
  232.  
  233.     tp_base    base.m        symbols used locally, or deliberately
  234.                 hidden, which are involved in the
  235.                 basic structure of the scenario.
  236.  
  237.     tp_graphics graphics.m    symbols used locally in the graphics
  238.                 code, such as local subroutines, state
  239.                 properties, etc.
  240.  
  241.     tp_util    util.m        local routines and flags used by the
  242.                 utility code exported from this file.
  243.  
  244.     tp_verbs    verbs.m     contains the routines which implement
  245.                 the standard verbs in the scenario, as
  246.                 well as their subroutines and status
  247.                 properties.
  248.  
  249.     tp_chat    chat.m        private symbols for implementing the
  250.                 chat and pose code and verbs.
  251.  
  252.     tp_quests    quests.m    private functions, etc. used in
  253.                 implementing the quest code, Questor,
  254.                 and Questor's Office.
  255.  
  256.     t_fight    combat.m    defines the properties and routines
  257.                 used for combat and monsters. Also
  258.                 builds on the store code to build
  259.                 armouries.
  260.  
  261.     t_monsters    combat.m    defines a few standard monsters as
  262.                 well as some slightly non-standard
  263.                 ones, and the code to support them.
  264.  
  265.     tp_fight    fight.m     functions and properties used internal
  266.                 to the combat and monster code.
  267.  
  268.     tp_monsters monsters.m    some routines relating to specific
  269.                 monsters defined here.
  270.  
  271.     t_mall    town.m        public rooms, etc. exported from the
  272.                 mini-mall area.
  273.  
  274.     t_streets    town.m        pubic locations, etc. exported from
  275.                 the streets in the town.
  276.  
  277.     tp_mall    mall.m        private rooms, code, properties, etc.
  278.                 used in the mini-mall area.
  279.  
  280.     tp_streets    streets.m    private rooms, code, etc. used in
  281.                 creating the streets of the town.
  282.  
  283.     tp_squirrel squirrel.m    rooms, objects, properties, code, etc.
  284.                 used in creating the squirrel area and
  285.                 quest.
  286.  
  287.     tp_machines machines.m    private code and properties used in
  288.                 creating the non-player characters in
  289.                 the town area (Packrat and Caretaker),
  290.                 and the enter-exit machine.
  291.  
  292.     tp_mail    mail.m        private code, properties, etc. used to
  293.                 implement in-MUD mail, bulletin boards
  294.                 and Postman.
  295.  
  296.     tp_proving    proving.m    private symbols global to the entire
  297.                 Proving Grounds area, or at least
  298.                 shared between more than one source
  299.                 file.
  300.  
  301.     tp_proving0 proving0.m    private stuff for surface level.
  302.     tp_proving1 proving1.m    private stuff for sewers level.
  303.     tp_proving2 proving2.m    private stuff for deep sewer level.
  304.     tp_proving3 proving3.m    private stuff for caves level.
  305.     tp_proving4 proving4.m    private stuff for chasm area.
  306.     tp_proving5 proving5.m    private stuff for doors room area.
  307.     tp_proving6 proving6.m    private stuff for 3-D maze area.
  308.  
  309.     tp_build    build.m     private stuff shared between files
  310.                 which implement the build commands and
  311.                 the Builder's Guild.
  312.  
  313.     tp_build1    build1.m    private stuff for the textual build
  314.                 commands, the build action "compiler",
  315.                 and some common build utilities.
  316.  
  317.     tp_build2    build1.m    private stuff for the button-driven
  318.                 build commands.
  319.  
  320.     tp_bguild    bguild.m    private stuff used when implementing
  321.                 the Builder's Guild area.
  322.  
  323.     tp_news    usenet.m    private stuff used to implement the
  324.                 usenet news reading stuff, the
  325.                 NewsRoom, and some code in common
  326.                 between it and the Telegram Office.
  327.  
  328.     tp_email    email.m     private stuff used to implement the
  329.                 email interface and the Telegram
  330.                 Office and its commands.
  331.  
  332. As you can see, most of the tables are private, and are only relevant
  333. to a small portion of the entire scenario. Some tables have hundreds
  334. of symbols, while others have under a dozen. Further partitioning of
  335. some of the public tables may be wise if many more symbols need to be
  336. added to them.
  337.  
  338.  
  339. Basics - base.m
  340.  
  341.  
  342. This file defines a large number of properties. They are divided up
  343. into related groups, and there are a reasonable number of comments in
  344. the file, so I won't go into detail on most of them.
  345.  
  346. The first symbol defined in the file is public symbol "MAX_CARRY".
  347. This is written in all capital letters to signify (to the reader, not
  348. to the AmigaMUD system) that it is a numeric constant. This is a
  349. stylistic thing used in other languages such as "C". Changing this
  350. constant before rebuilding the scenario will change the maximum number
  351. of things that a player can carry in his/her inventory. The limit is
  352. enforced by utility routine "CarryItem" in file "util.m". This routine
  353. is called by the "get/pick-up" verb code to actually do the final step
  354. in picking something up and adding it to the player's inventory.
  355.  
  356. This is the first of many cases where the code to do something in the
  357. actual scenario is more complicated than examples that have been given
  358. earlier, in other documents. This is because a real scenario wants
  359. things to be more realistic or interesting. In picking something up,
  360. for example, the code must allow for objects that cannot be picked up,
  361. objects that do something when they are picked up, rooms that do not
  362. allow objects to be picked up in them (glue on the floor?), players
  363. that cannot pick things up ("toaded"?), etc. This sort of complexity
  364. is often needed, and must be planned carefully so as to be
  365. consistently done throughout the scenario. The current versions of
  366. this in the standard scenario are probably not fully consistent, even
  367. though they have already gone through a couple of revisions.
  368.  
  369. On the other hand, it is easy to think of things that could be done
  370. relating to carrying things that the standard scenario does not do.
  371. Perhaps each object should have a weight, or a size, or both, which
  372. govern how they must be picked up. The weight of a container would be
  373. the weight of the empty container plus the weight of whatever is in
  374. it. The size of a container may or may not change as things are put in
  375. it, depending on whether it is something like a canvas sack or
  376. something like a bank vault.
  377.  
  378. The next few properties defined in "base.m" are entered directly into
  379. the public symbol table. This is a very unusual circumstance - most
  380. symbols would be entered into a specific table appropriate to them.
  381. The public table is used in this case since the properties defined in
  382. this group (p_rName, p_rContents, p_pCarrying, p_oName, p_oCarryer,
  383. p_oCreator, p_oContents) are referenced by code automatically
  384. generated and compiled by the build commands. Since someone using
  385. those commands should not be expected to have the appropriate tables
  386. in use, and since there is no safe way for the code itself to use and
  387. unuse the tables, these symbols are put into the public table, which
  388. makes them always available.
  389.  
  390. As with the names of tables, I have been picky about the names of
  391. properties. Property names all start with "p_", usually followed by a
  392. letter indicating where the property is intended to be used, followed
  393. by a descriptive name for the property. This convention is established
  394. in an empty database produced by MUDCre, since that database contains
  395. properties p_pName and p_pIcon. Properties intended for use on players
  396. (and thus by extension on all characters) use the letter "p".
  397. Properties intended for use on rooms use the letter "r", and
  398. properties intended for use on objects use the letter "o". The
  399. scenario is not quite fully consistent in that use - some properties
  400. that are used in more than one circumstance either have one letter
  401. from the possible set of appropriate ones, or have no letter at all.
  402. Sorry about that - there aren't many such cases.
  403.  
  404. It would be possible to write a short essay about the intended use for
  405. many of the properties that are defined here. That might be useful,
  406. but most people would never be able to read through it all, and I
  407. doubt if I would have the patience to type it all. Instead, I will
  408. assume that most readers will be able to figure a lot of things out
  409. for themselves, based on the comments in the files, the actual code in
  410. the files, and the other general material in this file. To show the
  411. flavour of the details, I will describe a few properties more fully.
  412.  
  413. Property "p_rBuyList", of type "list thing", is the list of items that
  414. are for sale in a store. Each thing in the list should be the model of
  415. the item for sale, from which the store code will clone (via
  416. inheritance) the actual items purchased by players. Each item for sale
  417. should have a "p_oPrice" property, indicating how many Blutos it
  418. costs. Some may have a "p_oBuyChecker" property, which the store code
  419. will call when the purchase is attempted. This code can do special
  420. initialization of the newly purchased item, etc.
  421.  
  422. "p_rNameAction" is a property that is defined and implemented in the
  423. standard scenario, but is not currently used. It allows the name of a
  424. room to be generated by an action, rather than being a simple string
  425. attached to the room. It was in fact used long ago, before inheritance
  426. was added to objects, as a way of avoiding string duplication.
  427.  
  428. "p_rDark" is a boolean property which, if set to "true" on a room,
  429. indicates that it is dark in that room. All aspects of the scenario
  430. pay attention to whether or not it is dark in the current room. For
  431. example, the autographics code will not draw a view of the room if it
  432. is dark. There are many ways to bring light into a dark room. They
  433. are: the presence of an agent who glows, the presence of an object
  434. which glows, or the presence of an agent who is directly carrying an
  435. object which glows. This testing is all done by some routines defined
  436. and exported in file "util.m". The expense of this test was the main
  437. reason the various "FindAgentXXX" builtin functions were created.
  438.  
  439. "p_rNoMachines" is a flag which can be set on a room. If it is
  440. present, the standard non-player characters will not enter that room
  441. by themselves. This is useful when keeping them out of areas where
  442. they would be a nuisance, or would perhaps interfere with the solving
  443. of quest (e.g. by making it easier!). An example of this possible
  444. subtle interference is the fact that Caretaker carries his prized
  445. flashlight, and therefore lights up any dark room he enters.
  446.  
  447. Property "p_rLocked", when set on a room, prevents anyone other than
  448. the owner of the room (and SysAdmin) from entering the room. This is
  449. useful to builders when they are still in the middle of setting up an
  450. area - they can keep it locked until it is all finished, then they can
  451. unlock it and invite other players in. If the area has only one
  452. connection point to the rest of the main scenario, then only the one
  453. connecting room needs to be locked.
  454.  
  455. Properties "p_rDesc" and "p_rDescAction" provide the long description
  456. for a room. A simple string description is stored in p_rDesc. A more
  457. complex, varying description, generated at run time, can be returned
  458. by an action stored as property p_rDescAction in the room. In the
  459. Proving Grounds, the description for the room with 5 doors varies,
  460. depending on which of the doors are open. This is done via an action
  461. attached as its p_rDescAction.
  462.  
  463. Property "p_rScenery" is used for "scenery words" attached to rooms.
  464. Scenery words are words which are the names of things that the player
  465. might try to examine or act upon, and should normally be present,
  466. depending on the description of the room, but which are not
  467. implemented in the scenario. For example, all outdoors locations
  468. should respond in some way to "Examine the ground." Responding "There
  469. is no ground here." is not very friendly. By placing a p_rScenery
  470. property on the generic room "r_outdoors", which contains the string
  471. "ground,sky", the scenario is able to realize that it is reasonable to
  472. ask about "ground" and "sky", and to respond "You see nothing special
  473. about the ground." Similarly, attempts to operate on the ground or sky
  474. will fail, but not complain about them not being there. The string
  475. used as p_rScenery is in the standard internal name form, and is
  476. matched against using "MatchName".
  477.  
  478. Property "p_rExits" is a list of integers which indicate which of the
  479. possible exits from the current room is considered to be an "obvious"
  480. exit, i.e. one which the player can see. All exits from rooms, whether
  481. obvious or not, are stored as links to other rooms. Those whose
  482. direction is not in p_rExits do not show up in the room description.
  483. This allows rooms to have hidden exits.
  484.  
  485. Property "p_rLastVisit" does not have any fixed purpose. It is used in
  486. the standard scenario in a couple of rooms in the Proving Grounds, to
  487. indicate the time (as returned by "Time") when the special actions in
  488. the room were last triggered. The actions will not trigger again until
  489. a certain time interval has passed. The general utility routines in
  490. the scenario do not use this property directly, so area-specific code
  491. can use it for whatever it wants.
  492.  
  493. Properties "p_rNoGoString" and "p_rNoGoAction" are a string and an
  494. action which are used when the player tries to go in a direction that
  495. does not have an exit in the room. If neither of these are present,
  496. the standard code prints the message "You can't go in that direction."
  497. The first, as usual, is a string that will be printed instead of the
  498. standard message, and the second is an action that will be called to
  499. do whatever it wants (like penalize a player for stepping off of a
  500. cliff). The NoGoAction does not return anything.
  501.  
  502. "p_rBuyAction" is a fairly simple property. If it is present in a
  503. room, then that room is considered to be a store, and should have a
  504. corresponding "p_rBuyList". The action is called when the player uses
  505. the "buy/purchase" command in the room. File "util.m" provides routine
  506. "StoreBuy", which is an appropriate BuyAction. Other, special purpose,
  507. ones are also possible.
  508.  
  509. This ends the block of properties that is described in some detail
  510. here. For the many other properties that are defined, the reader is
  511. encouraged to use the CLI "Search" command (or a "grep" program) to
  512. look for their occurrences in the source files. Studying the code that
  513. uses the properties should reveal what the property is all about.
  514. Often, the name of the property gives enough information to know
  515. whether or not it will be relevant to the problem at hand.
  516.  
  517. The next sets of properties defined in "base.m" come in groups of
  518. closely related properties. The first set, starting with "p_rNorth"
  519. are the properties used for the actual links between rooms. If
  520. property "p_rNorth" of room A is a reference to room B, then a player
  521. moving north from room A will move to room B (provided none of the
  522. various special actions occur). The use of the other 11 properties in
  523. the group is analagous.
  524.  
  525. The next set of properties are simple string properties which can
  526. contain a special description seen if the player looks in that
  527. particular direction (as in "look north"). The next set of
  528. descriptions ("p_rNorthMessage", etc.) are messages which are shown to
  529. the player when he/she moves in the corresponding direction from the
  530. current room. Following those are a set of messages
  531. ("p_rNorthOMessage," etc.) which are given to others in the room when a
  532. player leaves in the corresponding direction. Lastly comes a set of
  533. messages, starting with "p_rNorthEMessage", which are displayed to
  534. others in a room when a players enters the room from the corresponding
  535. direction. All of these properties are ones which can be set by
  536. builders.
  537.  
  538. The next sets of properties introduce the concept of "checkers". These
  539. are lists of routines that are called when a certain action (like
  540. going north) is attempted. Each routine in the list is called in turn
  541. and can OK the action (return "continue"); prevent the action (return
  542. "fail"); or complete the action (return "succeed"), thus cancelling
  543. any further checker actions and the default action. Many checker
  544. actions can be created in a simple form using the build code. Note
  545. that the checker properties defined here are put into table tp_base,
  546. which is private to SysAdmin. This is because they are only used by
  547. other routines later in this same source file.
  548.  
  549. The concept of checker lists came about quite early in the development
  550. of the standard scenario, and has not been as useful as I first
  551. thought it would. The idea was to allow several wizards to
  552. independently add checks to actions (usually moving in a given
  553. direction from a room), without worrying about whether or not anyone
  554. else had already done so, or might need to do so in the future. For
  555. example, several wizards might be involved in the north connection
  556. from room A to room B. Wizard One does not let anyone enter room B
  557. from that direction unless that person is carrying the Magic Gizbot.
  558. Wizard Two, who owns room A, does not let anyone leave through the
  559. north exit if that player is carrying the Sacred Shirt. Wizard Three
  560. has created a large box, which players can push around to block exits,
  561. and it is now blocking the north exit from room A. Thus, if a player
  562. tries to move from room A to room B the three checkers, together, will
  563. require that the player first move the large box, not be carrying the
  564. Sacred Shirt, and be carrying the Magic Gizbot. Because the current
  565. scenario is all written by one person, these situations have not yet
  566. arisen, but the provisions for them are present in the scenario.
  567.  
  568. Checker lists can exist for verbs as well as for room exits. Note that
  569. there are checkers for "AnyEnter" and "AnyLeave". These are triggered
  570. when a player tries to enter or leave a room, regardless of which
  571. specific direction is used. Note also that there are AnyEnterActions
  572. and AnyLeaveActions as well. These are simply lists of actions that
  573. are all called when the corresponding event occurs. Leave checkers and
  574. actions are called while the player is in the old room, Enter actions
  575. are done when the player is just in the new room. Enter checkers are
  576. done to see if something special, like an instant teleport out, should
  577. happen to the player in the new room. There are also special movement
  578. checkers that can be attached to the players. This great complexity on
  579. moving is used to implement a variety of things, such as automatically
  580. created monsters, graphics update on clients in the old and new rooms,
  581. healing on the player, etc.
  582.  
  583. Note that all of these checker properties are private to table
  584. tp_base. This means that no-one other than SysAdmin can directly
  585. access these properties. Instead, they are handled by a set of
  586. routines located further down in source file "base.m", which allow
  587. their callers to add, remove, and processes these checker lists. This
  588. shows how the concepts of "modularity" and "information hiding" can be
  589. done in AmigaMUD. My initial goal in doing the checkers in this way
  590. was to reduce the number of symbols that needed to be exported. As you
  591. can see, I did not succeed! Perhaps a better way would be to have
  592. fewer interface routines, that are passed the direction number, rather
  593. than having individual routines for each direction.
  594.  
  595. The next large set of properties are properties that can be attached
  596. to players (and often to machines). I will not describe them in detail
  597. here - if a property's name suggests that it might be of use in what
  598. you are trying to do, then search for it in the source files, as
  599. mentioned above. After the player properties comes a similar set of
  600. properties for use on objects. These include things like the object's
  601. name and description, anything that can be read on it, checker lists
  602. for some important actions, etc.
  603.  
  604. The various POS_XXX constants, placed into table tp_misc, are used to
  605. indicate a descriptive position on a player. For example, if the
  606. player sits down on a chair, then his position will be POS_SIT_ON, and
  607. the property "p_pWhere" will be the chair. A player must stand up (or
  608. whatever) before assuming any other position, or leaving the room.
  609.  
  610. The three EFFECT_XXX values are defined here for convenience. The
  611. standard scenario does not currently try to maintain any ongoing sound
  612. effects, but it does cancel the speech when the player walks out of
  613. the bank or Beauty Shop.
  614.  
  615. The next few lines (the first actual code in the scenario!) create a
  616. generater of unique integer values. The generator is a thing, which
  617. has only one property. This property is initially zero. Every time
  618. someone calls function "NextSoundEffectId", the code will increment
  619. the value of that property and return the new value. A similar
  620. generator is present in file "graphics.m" for use in creating effects
  621. ids. Such a generator should not normally be used at runtime (when
  622. players are triggering things), but instead should be used when the
  623. scenario is being created, to assign a new, fixed identifier for some
  624. effect. They are then somewhat like an enumeration datatype, except
  625. that the number of elements in the enumeration is not determined until
  626. the entire scenario has been built, and all identifiers assigned.
  627.  
  628. The "After" builtin in AmigaMUD schedules a call to an action for a
  629. point in the future. The action will be run on behalf of the agent
  630. (player character or machine) which called "After". Sometimes a time-
  631. scheduled event is desired, but there is no convenient agent to do it
  632. with. The "TimeKeeper" machine provides a general service for doing
  633. timed events, independent of other machines. The timed events that it
  634. governs are continued across server shutdowns by the "machine idle"
  635. and "machine active" handlers that the TimeKeeper uses.
  636.  
  637. I will not attempt to explain the operation of the TimeKeeper here -
  638. it is one of the more complex systems in the scenario. It uses a
  639. linked list of things, sorted by the time of their triggering. The
  640. interface to the TimeKeeper is as follows:
  641.  
  642.     DoAfter(int seconds; thing object; action a)void
  643.  
  644.     A call to DoAfter will set things up so that "seconds" seconds
  645.     of server up-time after the call, action "a" will be called on
  646.     behalf of the owner of "object". It will be called with
  647.     "object" as its parameter. The action will be called
  648.     regardless of any server shutdowns and restarts which occur in
  649.     the waiting period. This facility is useful for events which
  650.     occur in real scenario time, such as torches burning out, etc.
  651.  
  652.     CancelDoAfter(thing object; action a)bool:
  653.  
  654.     CancelDoAfter will search for a timed event which is scheduled
  655.     to call action "a" for thing "object". The first (earliest)
  656.     such event (if any) is cancelled. If such an event is found
  657.     and cancelled, CancelDoAfter returns "true", else it returns
  658.     "false".
  659.  
  660. When a player clicks on one of the standard movement mouse-buttons,
  661. clicks in the map-view area to move the cursor, or uses a numeric
  662. keypad key to move, the scenario echoes the command being done before
  663. executing it. This is done using routine "InsertCommand", which is
  664. passed the string of the command to be executed. It simply prints the
  665. command, preceeded by ">>> ", and then passes it to "Parse", the
  666. builtin function which parses input commands. Note that this direct
  667. call to Parse means that the given command cannot be a "say" or "pose"
  668. entered using the single-character forms (quote and colon), and any
  669. aliases in it will not be expanded.
  670.  
  671. Recall the earlier discussion of checker routines attached to some
  672. objects and to some room exits. They are a list of actions, which are
  673. called one-at-a-time to determine if the action is allowed to happen.
  674. The next routines in "base.m", "DoChecks", "DoThingChecks",
  675. "DoStringChecks", and "DoIntChecks" are the routines used to execute
  676. the checker lists. The ones with a type in their name are also passed
  677. an additional parameter of that type, which is passed to each action
  678. in the list which is called. Following those are a couple of other
  679. related routines. "DoList" simply executes each action in a list of
  680. actions. "DoThingActions" is similar, except that it has an additional
  681. thing parameter which is passed to each action called.
  682. "DoActionStrings" is also similar, except that each parameterless
  683. action in the list is expected to return a string, and all of the
  684. resulting strings are joined together and the combined result is
  685. returned. Note that all of these routines call "SetEffectiveToNone"
  686. before processing the list. This removes the access-rights of whoever
  687. is calling the routine, so that if an action in the list is "utility",
  688. it will not inherit the access rights of that caller.
  689.  
  690. Next in the file come a number of symbols which relate to directions
  691. in the scenario. The available directions are numbered, so that they
  692. can be stored as properties or lists (as in p_rExits), and so that it
  693. is easy to find which direction is "right" or "left" of another.
  694. First, a set of constants naming the directions is defined. "DirBack"
  695. is a utility routine which takes a direction number and returns the
  696. direction number of the reverse direction. "DirName" takes a direction
  697. number and returns a string naming that direction in a form which can
  698. be used in "Fred has left to the <direction-name>". "ExitName" is
  699. similar, except that it just returns the one-word name of the
  700. direction. "DirProp" takes a direction number and returns the property
  701. which is the link from a room in that direction. This is used in the
  702. build code, to enable it to work easier with variable directions.
  703. Similarly, "DirChecks" returns the property which gives the checker
  704. list for the corresponding direction. "DirDesc" returns the property
  705. which describes the corresponding direction, "DirMessage" returns the
  706. property which contains the message shown to a player leaving by the
  707. direction, "DirOMessage" returns the property which contains the
  708. message shown to other players in the source room when a player
  709. leaves, and "DirEMessage" returns the corresponding property which
  710. contains the message shown to players in the destination room when a
  711. player arrives from the passed direction. Note that the relationship
  712. between the directions in the last two is usually that given by
  713. DirBack, unless the scenario is mazelike in its construction.
  714.  
  715. DirMatch is essentially the reverse of DirName. It takes a direction
  716. name and tries to return a corresponding direction number. This is
  717. done using builtin "MatchName", so that several alternative forms of
  718. the direction names can be accepted. If DirName cannot find a match
  719. for the direction name, it will return -1, which is exactly what
  720. MatchName does in that circumstance. The last direction-related
  721. function here is "PairToDir", which returns a direction number based
  722. on a two-character code for the direction. These two-character codes
  723. can be stored in a string which represents the path for a machine to
  724. take.
  725.  
  726. When an action is to be added to a checker list, the order of the
  727. actions in the list may be important. In the example given previously,
  728. the order of the checks should be: first the box, then the check of
  729. leaving this room, then the check of entering the destination room. To
  730. help in getting things in the right order, routine "AddChecker", which
  731. is used to add actions to checker lists, takes a bool parameter,
  732. "front" which says whether to add the new action to the front of the
  733. list (so it will be executed first), or to the back of the list (so it
  734. will be executed last). AddChecker does not take a checker list as its
  735. parameter. Instead, it takes a thing and a property which is the
  736. checker property. This way it can create and add a new checker list if
  737. there wasn't one already there. "delChecker" is the reverse of
  738. AddChecker - it deletes a checker routine from a list. It will delete
  739. the entire list from the thing if the list becomes empty. Note that
  740. both of these routines are private to table tp_base - they are only
  741. called from the routines which follow.
  742.  
  743. Next in file "base.m" are a number of routines which add and delete
  744. specific checkers from rooms, objects and characters. They are all
  745. very short - just calling either AddChecker or delChecker with the
  746. appropriate checker property. Following are the similar routines which
  747. actually execute the checker lists, returning the final result that
  748. they get. These must be in this source file since the properties for
  749. the checkers were made private. I now believe that doing so was a
  750. mistake - the properties should be public, and all these silly little
  751. routines should be done away with.
  752.  
  753. The final routine in "base.m" is "SetupMachine". It is just a little
  754. routine which will carefully initialize a machine being used as a non-
  755. player character. It adds empty carrying and hidden lists, if they do
  756. not already exits. The hidden list can be used to attach things to a
  757. character in such a way that they do not show up in the inventory, and
  758. cannot be dropped in the normal way by the player.
  759.  
  760.  
  761. Basics - icons.m
  762.  
  763.  
  764. This short source file contains some handy routines for dealing with
  765. icons. Most of the code is pretty straightforward. The first set of
  766. routines are the most used ones. "ShowIconOnce" shows the icon of the
  767. agent whose thing is passed as a parameter to the current agent.
  768. "ShowIcon" uses ForEachAgent to call ShowIconOnce for each agent,
  769. other than the active one, in the same room as the active agent. The
  770. effect of this is that the active agent's icon will appear on the
  771. screen of each player in the same room as the active agent. The active
  772. agent can be a player character or a non-player character.
  773. "DoShowIcon" does nothing other than call ShowIcon. It returns a
  774. "status" result, however, and so is of the right type to be called by
  775. ForceAction. So, it can be used when forcing an agent to move
  776. somewhere, to get that agent's icon shown properly.
  777.  
  778. The next set of routines is the reverse of the first set. They are
  779. used for removing icons from displays. The fact that "UnShowIconOnce"
  780. has more comments that code indicates that it took me a while to get
  781. it right!
  782.  
  783. "PrintIcon" isn't actually used in the scenario. It is a handy utility
  784. to dump out the internal array of an icon, however. As the comment at
  785. the top of the file indicates, the various makeXXXIcon functions were
  786. generated by a simple program which contained the icon represented as
  787. an array of 16 16-bit words, represented in hexadecimal. Once again I
  788. beg for some better icons.
  789.  
  790.  
  791. Basics - graphics.m
  792.  
  793.  
  794. This file is much longer than the previous one. It contains various
  795. utilities, as well as the icon/cursor editor and all of the
  796. autographics code. The properties at the beginning are mostly attached
  797. to rooms to indicate how the rooms are to be drawn:
  798.  
  799.     p_rName1 - the first of a two-line room name, or a one-line name
  800.     p_rName2 - second line of two-line room name
  801.     p_rBackGroundPen - colour for background
  802.     p_rForeGroundPen - colour for main foreground drawing
  803.     p_rEdgePen - colour for edge of rooms
  804.     p_rDoorPen - colour for doors in "door rooms"
  805.     p_rCursorX, p_rCursorY - where to put player cursor for this room
  806.     p_rDrawAction - special routine to draw the room
  807.     p_pStandardButtonsNow - standard movement buttons are active
  808.     p_rAutoGraphics - use autographics for this room
  809.     p_rAutoDrawAction - autographics function to use
  810.  
  811. Next comes names for the default colours that the Amiga MUD client
  812. program uses for the graphics area. "ColourMatch" takes a string which
  813. should be the name of a colour, and tries to understand it as the name
  814. of one of the standard colours. It returns the corresponding colour
  815. code, or -1 if nothing matches. The special cases at the beginning are
  816. to avoid MatchName finding the wrong match because of an ambiguous
  817. name. "ColourName" is the reverse - it returns the name of the passed
  818. colour code. "ShowKnownColours" can be used in error messages - it
  819. simply prints a message indicating what the standard colour names are.
  820.  
  821. A "map group" is an integer value which identifies a displayed map,
  822. such as the standard scenario displays for the mini-mall, the streets,
  823. and the Builder's Guild. When moving from room to room, if the map
  824. group changes, then the graphics must be redrawn. When moving between
  825. rooms with the same map group, only the cursor position and the room
  826. name must be redrawn.
  827.  
  828. "NextMapGroup" is a generator for map group values. It should be
  829. called once for each map image which represents more than one room.
  830. Similarly, "NextEffectId" returns unique identifiers for effects, as
  831. discussed in previous documents.
  832.  
  833. "DrawRoomNameBox" is the first of the utility routines which are
  834. available to deal with the display style that the standard scenario
  835. uses. It draws the box within which the room name is displayed, on the
  836. right half of the display, above the standard movement buttons. If the
  837. standard graphics style is in use, then this routine only needs to be
  838. called once at the beginning of the session. "DrawRoomName" draws the
  839. room name within that box. If its second parameter is an empty string,
  840. then it will center its first parameter in the box, as a one-line room
  841. name. If the second parameter is non-empty, then the two parameters
  842. are displayed centered horizontally within the room name box. Both are
  843. truncated if they are too long.
  844.  
  845. Next follows four routines which draw the little door indicators that
  846. the standard scenario uses. Note that each has a unique effect id,
  847. yielded by NextEffectId. Note also that none of the routines sets a
  848. drawing pen - they draw the door in the current pen. Following is
  849. function "MakeCursor", which simply returns the definition of the
  850. standard little smiley-face cursor. "EraseAllButtons" simply calls
  851. ClearButtons, which removes all mouse-buttons from the active client.
  852. Builtin functions cannot be attached to things as properties (they
  853. cannot be used as first-class values at all), so a interpreted routine
  854. is needed for that purpose.
  855.  
  856. The next set of routines creates and handles the standard set of mouse
  857. buttons (movement and look-around) and the standard mouse-click region
  858. which is used to allow the player to click near the cursor to move in
  859. that direction relative to the current room. There is nothing much of
  860. interest in this code. It is fairly basic to the scenario, however.
  861.  
  862. The code starting with "EnterRoomDraw" is more interesting. This is the
  863. code which provides graphics from rooms in the scenario. EnterRoomDraw
  864. is called when a player enters a room, and the graphics for that room
  865. are to be drawn. The two portions of code handle the cases of rooms
  866. with autographics and rooms with custom graphics. In both cases,
  867. although with minor differences, the first thing to be checked is
  868. whether the move is into a room with a different set of graphics,
  869. determined by comparing the map group stored on the player against the
  870. map group of the room being moved into (obtained from "Here()"). If
  871. the graphics set is different, or this is the first time that graphics
  872. is being drawn, then the code first clears the map area of the
  873. display. For an autographics room, the autographics routine is
  874. retrieved and called. Then, the cursor is placed in the center of the
  875. room (standard for all autographics), and the room name is displayed.
  876. This is either obtained from the p_rName1 and p_rName2 properties, or
  877. is just the last word of the room name, capitalized.
  878.  
  879. For rooms with custom graphics, the procedure is quite similar. First,
  880. the area is initialized if it was not already. Then, if the new room
  881. has a different map group then the previous room, the area is cleared,
  882. the custom drawing routine is called, and the map group of this room
  883. is stored on the player. Next, the cursor is placed in the indicated
  884. position, if any position is indicated. Finally, the room name is
  885. drawn. Note that a custom graphics room is assumed to have a specific
  886. name set.
  887.  
  888. "LeaveRoomDraw" is called when the player leaves a room which has
  889. graphics specified. The current version of the standard scenario has
  890. graphics specified for all rooms, so this routine doesn't ever do
  891. anything, but in the past there were areas that didn't have graphics.
  892. It essentially zaps everything that needs zapping.
  893.  
  894. "RoomGraphics" is a standard routine which is useful when creating
  895. rooms in the scenario that have custom graphics. It is passed the
  896. room, the two strings for its name, its map group, the position of the
  897. cursor in the room map for this room, and the custom drawing routine
  898. to use to draw the room. Any autographics on the room is removed, any
  899. non- default parameters are stored on the room, and the room is given
  900. EnterRoomDraw and LeaveRoomDraw as the code to use to draw it. No
  901. rooms in the current scenario have any other routines, but in the past
  902. there were non-graphics area that had no such routines.
  903.  
  904. "AutoGraphics" is similar to RoomGraphics, except that all it takes is
  905. the autographics function to attach to the room. This function is
  906. normally one of the autographics routines provided in this file.
  907. AutoRedraw is a utility routine which is used by the build code. It
  908. simply erases the map display and redraws it. This is useful when
  909. something affecting the autographics display (like a colour choice)
  910. has been changed. AutoPens allows setting of the pens to use for the
  911. autographics display of the room. It conserves space by deleting any
  912. existing pen when the desired pen-colour is 0. As discussed in other
  913. documents, an int property fetched from a thing which does not contain
  914. that property will retrieve the value 0. RoomName simply sets one or
  915. two room name strings.
  916.  
  917. "DrawUpArrow" and "DrawDownArrow" are the first parts of the
  918. autographics routines. They can be called from custom drawing code as
  919. well. They are passed the colour to draw the arrows in, which is
  920. normally "gold" for the autographics code. "DrawUpDown" takes the room
  921. and the list of obvious exits from the room (normally obtained from
  922. the room itself), and draws any needed up and down arrows. It does a
  923. bit of checking to pick a good colour to draw them in.
  924.  
  925. The next few hundred lines of code are the autographics routines. Feel
  926. free to add more. Essentially, autographics is an automatic way to
  927. produce simple graphics for any room. Autographics only looks at the
  928. obvious exits from a room - "hidden" exits are not shown. Autographics
  929. draws the view based on the exits it has to display, and will select
  930. the colours to draw in from the room, or will use default colours. The
  931. available styles of autographics can be seen by playing around with
  932. the mouse-button build code. They are:
  933.  
  934.     AutoRoads - wide tan regions on a dark green background.
  935.  
  936.     AutoPaths - narrow tan regions on a forest green background.
  937.  
  938.     AutoTunnels - medium-wide light grey regions on a dark grey
  939.     background.
  940.  
  941.     AutoTunnelChamber - a central light grey octagon on a dark grey
  942.     background. Exits are medium-wide light-grey regions extending
  943.     from the octagon to the appropriate side or corner. Function
  944.     DrawTunnelChamber is separated out to make it callable from
  945.     custom graphics routines.
  946.  
  947.     AutoOpenSpace - the map region is filled with the foreground
  948.     colour, and then any direction which has an exit is filled
  949.     with the background colour. Not very pretty. Intended for
  950.     things like fields.
  951.  
  952.     AutoOpenRoom - all drawing uses a single-pixel edge between the
  953.     foreground parts and the background parts. The basic shape is
  954.     that of a rectangle, with extra rectangles added as passages
  955.     to other rooms.
  956.  
  957.     AutoHalls - similar to AutoOpenRoom, except that there is no open
  958.     rectangle in the middle - just passages. Diagonal exits are
  959.     not displayed - I couldn't figure out how to make them look
  960.     reasonable in this style.
  961.  
  962.     AutoClosedRoom - a rectangular box with doors indicated in the
  963.     four orthogonal directions as needed. Doors in the diagonal
  964.     directions are drawn by nibbling off a bit of the corner, so
  965.     that the doors can be fit in.
  966.  
  967. Following the autographics routines is the icon/cursor editor.
  968. "showIconPixel" is a local subroutine which will display the current
  969. contents of the indicated pixel. Each pixel is shown as a small
  970. rectangle in the main editing area, as well as a single pixel in the
  971. area which shows the icon/cursor at normal size. The value to draw for
  972. the pixel is retrieved from the active agent, based on the passed co-
  973. ordinates of the pixel. "showWholeIcon" simply loops over the rows and
  974. columns of the icon, using showIconPixel to display each pixel.
  975.  
  976. "iconEditMouseHandler" is the handler for mouse-click-in-region events
  977. which is used to implement the icon editor. It simply determines which
  978. pixel is affected and toggles it in the bit array.
  979. "iconEditButtonHandler" handles the three buttons which the icon
  980. editor puts up: CLEAR - clear the icon to all black, DONE - save the
  981. current icon value as the final value, and exit from icon editing, and
  982. CANCEL, which puts the icon back the way it was before beginning
  983. editing, and exits from editing. The latter two also remove the icon
  984. editor buttons and mouse region from the display, and call a caller-
  985. supplied restore action to redraw the normal graphics view.
  986. "iconEditIdleHandler" is a catcher for when the user exits the MUD
  987. while in the middle of editing an icon or cursor. It simply undoes the
  988. values for handlers, etc. that were stored when the user entered
  989. editing mode, and calls whatever idle handler was in effect when
  990. editing was started. This matches the philosophy for idle handlers
  991. discussed in "ProgConcepts.txt".
  992.  
  993. "copyIcon", "startEdit", "StartIconEdit" and "StartCursorEdit" simply
  994. set things up for editing and do the initial display. Note that the
  995. latter two are the only functions exported from this icon editing code
  996. - the rest are simply internal routines used in the implementation.
  997.  
  998.  
  999. Basics - util.m
  1000.  
  1001.  
  1002. File "util.m" is a fairly long file. It provides a lot of the basic
  1003. utilities which are used all over the scenario. The stuff here is not
  1004. quite as basic as the stuff in "base.m", but the choice between
  1005. putting things in one file versus the other is pretty fuzzy.
  1006.  
  1007. When moving from room to room, there are a couple of ways the move can
  1008. be done. The normal way is to physically move from one room to
  1009. another. In that case, the exit actions and checks for the room being
  1010. left are executed, followed by those for the room being entered. The
  1011. occupants of the two rooms are informed of the move. Wizards and
  1012. builders can "poof" between rooms, however. This is a type of
  1013. magical teleportation that does not involve going between the rooms,
  1014. so any checkers are not executed. Also, the message given to those in
  1015. the rooms is different. A third possibility is that something in the
  1016. scenario is causing the move. For example, when opening some doors in
  1017. the scenario, the player is automatically moved into the room on the
  1018. other side of the door. In this case some of the checkers are not
  1019. executed, as required in the individual case. The first three
  1020. constants defined in "util.m", the "MOVE_XXX" values, are used to
  1021. inform some generic routines as to which kind of move is being done.
  1022.  
  1023. A set of standard rooms are defined next. These are stored in the
  1024. "t_roomTypes" table. They have the appropriate kind of autographics,
  1025. with the default colours. They also have a small amount of neutral
  1026. scenery. Programmers are requested to inherit from these generic rooms
  1027. when they build rooms of their own. If this is done, then code can be
  1028. written, e.g. on machines, such that it is easy to tell the general
  1029. kind of location that a machine is in, such as indoors versus
  1030. outdoors, forest versus roadway, etc.
  1031.  
  1032. When creating a source file for a new region of the world, there will
  1033. be one or more locations in the pre-existing world at which the new
  1034. locations will be attached. The description of those locations should
  1035. be updated to indicate the new additions. Routine "ExtendDesc" is
  1036. intended for that purpose. If the room passed has a description, then
  1037. the passed new description is added to the end of the existing one,
  1038. with a separating space. If the room has no description, then the new
  1039. one becomes the room's description. As an example, the Proving Grounds
  1040. is attached at one point to the pathway to the north of the town.
  1041. ExtendDesc is used to extend the description of the pathway to
  1042. indicate the new direction.
  1043.  
  1044. Function "Scenery" operates in a similar manner. The p_rScenery field
  1045. on rooms is one which is in the internal noun-phrase format. It
  1046. contains the names of objects which are mentioned in or implied by the
  1047. location's description and name, but which do not actually exist.
  1048. Using this technique, the scenario allows commands like "Get tree." to
  1049. reply "You cannot get the tree." instead of "There is no tree here."
  1050. when used in a location (like a forest) that should logically have
  1051. trees. New scenery is added to old scenery with a period as a
  1052. separator, since the checking of scenery is done with the "MatchName"
  1053. builtin, and that routine handles noun-phrases separated by periods.
  1054.  
  1055. Function "Sign" is a bit more complex. It builds a sign of some kind
  1056. in the room specified. The sign will not show up as an object in the
  1057. room, and cannot be taken away, but it can be examined and read. An
  1058. example call:
  1059.  
  1060.     Sign(Here(), "sign;small,wooden.sign;no,trespassing",
  1061.     "The wooden sign is very old, and appears to have been "
  1062.     "painted by hand. The lettering is quite hard to read.",
  1063.     "No Trespassing").
  1064.  
  1065. This will create a sign at the current location, which can be referred
  1066. to as "small wooden sign" or as "no trespassing sign". To let the
  1067. player's know that the sign exists, the description for the room
  1068. should contain something like "There is a small wooden sign at the
  1069. side of the road."
  1070.  
  1071. The next set of routines are useful for setting up rooms. They are
  1072. quite generic, so if you want a set of rooms with some common
  1073. properties, you may want to write some routines that are easier to
  1074. call for your special case. All of these routines add empty contents
  1075. and exits lists to the rooms. The scenario code assumes that these
  1076. lists exist on all rooms, so any other room-creating routines you
  1077. write should make sure they are present. The first pair of routines
  1078. take a name and a description for the room, and add them to the room.
  1079. If the passed description is an empty string, then no description is
  1080. added to the room. The scenario is set up to handle that case. The "P"
  1081. variant makes the room public, i.e. changeable by any wizard, whereas
  1082. the non-P variant makes the room private.
  1083.  
  1084. The next pair of routines is exactly the same, except that the rooms
  1085. they set up are made to be dark. Players will not be able to see what
  1086. they are doing in those rooms unless they, or some other agent or
  1087. object in the room, is providing light. The "SetupRoom2" versions take
  1088. an action as the room name, instead of a string. This was useful
  1089. before the AmigaMUD database supported inheritance, in order to cut
  1090. down on duplicated strings in the database. It is also useful for
  1091. special rooms where the name of the room will change depending on
  1092. circumstances. These rooms have no description. The "SetupRoom3"
  1093. versions are similar, except that they also require a description
  1094. string. Other variants can easily be created as needed.
  1095.  
  1096. "SetupObject" is used to initialize simple objects. Further setup of
  1097. the objects can be done after SetupObject is called. Using a routine
  1098. like this to setup an object (or a room), allows fewer lines of source
  1099. to be used to accomplish the setup. If SetupObject is passed a non-nil
  1100. value for "where", then it will add the object to the contents list of
  1101. that room, and set that room as the current location and the home for
  1102. the object. The home of an object is where Caretaker will leave the
  1103. object if he comes across it, and later its home.
  1104.  
  1105. "FakeObject" is similar, except that the object is more like a piece
  1106. of scenery - it is not gettable, and it does not show up directly in
  1107. the room's inventory. "FakeModel" is nearly the same as FakeObject,
  1108. except that it doesn't put the object anywhere. It's name reflects its
  1109. use in setting up things which will be inherited as parent by newly
  1110. created things, such as when something is purchased in a store.
  1111. Function "DepositObject" is one example of that action - it clones
  1112. (inherits from) a new object from a passed object, and deposits the
  1113. clone in the given room. The "p_oCreator" property of the new object
  1114. is set to be the active agent. Thus, the active agent owns the object,
  1115. in the sense of the scenario. In the sense of AmigaMUD, SysAdmin owns
  1116. the object.
  1117.  
  1118. The next set of routines are useful in setting up the links from room
  1119. to room. They also take care of properly setting reverse links (except
  1120. the UniConnect forms), and of adding the exits to the appropriate
  1121. exits lists (except the "H" forms, which create hidden connections).
  1122. The most commonly used version is "Connect". Connect can usually be
  1123. called from a single source line, and accomplishes several lines worth
  1124. of setup from the pair of rooms.
  1125.  
  1126. "ShowExits" is the first routine here that is useful from inside verb
  1127. routines. It simply shows a nicely formatted list of the obvious exits
  1128. in the room that is passed. Its output goes to the active client. Note
  1129. the use of SetIndent to format the list nicely. This is only seen in
  1130. the standard scenario in the exist list for the mini-mall room. Also
  1131. seen here is a call to ExitName, which was defined in "base.m".
  1132.  
  1133. "ShowList" is similar in nature. It is passed a list of objects, such
  1134. as a room contents list or a character inventory list. It prints that
  1135. list, indented by two spaces, with an introductory string printed
  1136. before the first item. Items are not printed if they are invisible.
  1137. The routine returns "true" if no objects were printed (which is not
  1138. the same as there being no objects in the list). In that case, the
  1139. caller can, if needed, print out a message indicating that nothing was
  1140. seen.
  1141.  
  1142. Routine "DoAll" is useful for verbs which wish to be able to apply
  1143. themselves to "all". It scans down the passed list of objects, calling
  1144. the passed routine on each visible one. The routine returns a bool
  1145. (true/false) value. If it ever returns "false", then DoAll will stop
  1146. its scan of the list and return "succeed". If there were no visible
  1147. objects on the list, then DoAll returns "fail". If nothing special
  1148. happened (all calls returned "true"), then DoAll returns "continue".
  1149. DoAll is setup so that the called action can delete objects (including
  1150. the current object) from the list, without too much confusion.
  1151.  
  1152. Function "CanSee" is fairly central to the operation of the scenario.
  1153. It reports whether or not the passed agent can see in the passed room.
  1154. If called for a simple monster (p_pStandard not set), then the action
  1155. is actually done for the active agent. If called with 'nil' as the
  1156. agent, then no specific agent is assumed to be in the room for the
  1157. testing. An agent can see in a given room if the room is not dark, if
  1158. any object in the room is emitting light (p_oLight), if any agent in
  1159. the room is emitting light, or if any agent in the room is directly
  1160. carrying an object which is emitting light. The desire to make this
  1161. routine less expensive was the incentive for implementing the various
  1162. "FindXXX" routines, three of which are used here.
  1163.  
  1164. "LightAt" is slightly different - it simply asks if the given room is
  1165. lit, without any special checks on any character. Similarly,
  1166. "HasLight" asks if the given character is a source of light,
  1167. regardless of which room the character is in.
  1168.  
  1169. Private routine "containsChild" is part of the implementation of
  1170. public routine "CarryingChild". It is recursive, i.e. it calls itself.
  1171. It returns the first object in the passed list that is a child of the
  1172. passed object. It also checks the contents of any objects which are
  1173. containers, and calls itself for each container. If no object on the
  1174. list is a child of the passed thing, and no object on the list is a
  1175. container which contains an object which is such a child (or is a
  1176. container which contains...), then "containsChild" returns 'nil'.
  1177. CarryingChild uses it to answer the question: does "who" have a
  1178. "what"? Similarly, "ChildHere" answers the question: is there a "what"
  1179. in room "room"?
  1180.  
  1181. Routine "ShowPosition" is used to print the description of how a
  1182. character appears in a room. It's output is intended to appear after a
  1183. prefix of the form "Player is ". If the player is simply present in
  1184. the room, then, the total output will be "Player is here.\n". If,
  1185. however, the player is using some furniture, then the output will be
  1186. of the form "Player is sitting on the sofa.\n". The standard scenario
  1187. does not attach much semantic meaning to such positioning - it is
  1188. mostly used as an atmospheric addition to player descriptions.
  1189.  
  1190. Routine "ZapObject" is a heavy-duty way of getting rid of an object.
  1191. It does this using the builtin "ClearThing", which removes all
  1192. properties from a thing. If the original object is a container,
  1193. however, then ZapObject recursively applies itself to all objects
  1194. contained in the container.
  1195.  
  1196. "SayToList" is a little routine which speaks a message to each room
  1197. whose thing is contained in a passed list. This is used in the "Doors
  1198. Room" area to make the various sounds audible throughout the locations
  1199. making up the doors room and the passage leading to it.
  1200.  
  1201. "CharacterDescription" returns a string which is the description of
  1202. the passed character. This can be either a simple string or can be the
  1203. result of calling a character's description action. This is made into
  1204. a routine since it is useful in situations like taking a photograph
  1205. and looking in a mirror.
  1206.  
  1207. "LookAtCharacter" uses CharacterDescription and makes other checks in
  1208. order to print to the active agent a description of some other agent.
  1209. It returns "true" if it is able to do so. It first checks for and
  1210. calls any "p_pDescCheck" to see if looking at the character triggers
  1211. some special case (such as looking at a medusa might). LookAtCharacter
  1212. also prints out the inventory of the character.
  1213.  
  1214. The next pair of routines in the file have to do with showing the set
  1215. of other characters that is present in a room. "ShowAgents" simply
  1216. calls "ShowOneAgent" for each other active agent in the room, using a
  1217. call to ForEachAgent. If the agent is not hidden (something only
  1218. wizards or apprentices can do in the current scenario), then a line
  1219. showing the agent is printed, and that agent's icon is also displayed.
  1220. Note that this code is called for both player characters and for non-
  1221. player characters.
  1222.  
  1223. The next six routines are used to show and unshow the room that an
  1224. agent is in from that agent. This is only needed for real players,
  1225. since showing a room to a machine doesn't accomplish anything. Routine
  1226. "ShowRoomToMe" is the bottom-level routine of the first set. It first
  1227. unconditionally prints the name of the room, preceeded by "You are ".
  1228. It then checks for and calls any "look checks" in the room, using the
  1229. routine exported from "base.m" for that purpose. If the checkers (if
  1230. any) do not block further looking, ShowRoomToMe next checks to see if
  1231. the character is wanting verbose descriptions. If so, then the rooms
  1232. description (either a string or the result of a p_rDescAction routine)
  1233. is shown. Then, if the player is not in superbrief mode, the obvious
  1234. exits from the room are shown. Next, ShowList is used to display the
  1235. non-hidden contents of the room. After that, if the active agent (the
  1236. one the room is being shown to) has graphics enabled, the set of icons
  1237. currently shown is removed, and any graphics display routine for the
  1238. room is called. Finally, ShowOneAgent is used to display the agents in
  1239. the room. Note that, in order to avoid the cost of one function call,
  1240. ShowOneAgent is used directly. ShowRoomToMe returns "false" if a look
  1241. checker prevented display of the room, and "true" otherwise. If the
  1242. look checker returned "succeed", indicating that it had already done
  1243. all needed displaying of the room, then only the contents list and
  1244. icons are displayed. "doShowRoom" is a local routine which simply
  1245. calls ShowRoomToMe. It is setup, however, to be callable from the
  1246. builtin routine ForceAction, which allows one agent to make another
  1247. agent do something. In turn, doShowRoom is called from the public
  1248. routine "ShowRoomToAgent", to allow using ShowRoomToMe to show the
  1249. room to another agent. This is used in situations where an action by
  1250. an agent causes the room to become lit, and hence visible to other
  1251. agents already in the room. The next three routines perform a similar
  1252. but reverse task - that of removing any graphics display of a room.
  1253. This can be used when the only source of light in a room is removed.
  1254.  
  1255. "EnterRoomStuff" is used for the second half of moving from one room
  1256. to another - that of entering the new room. First, any actions to be
  1257. triggered on room entry are called (actions checking to see if the
  1258. move is allowed have already been called). Next, we see if the room
  1259. was lit before the agent moves in. The line
  1260.  
  1261.     SetLocation(dest);
  1262.  
  1263. is the one that actually sets the location of the agent, as far as the
  1264. AmigaMUD system is concerned - all the rest, including all of the
  1265. routines called, are just for the scenario's benefit. If the room is
  1266. dark, and the active agent doesn't have a source of light (the test is
  1267. made directly for speed purposes), then the player is not shown the
  1268. room, and any graphics already displayed are removed by calling
  1269. UnShowRoomFromMe. If light is present, then the room is shown to the
  1270. agent. Then, if the agent is not hidden, the agent's icon is shown to
  1271. all other agents in the room, and they are shown an appropriate
  1272. message about the arrival of the agent. Finally, if the room was
  1273. previously dark, then ShowRoomToAgent is used to display the room to
  1274. each other agent in it. Note the double use of ForEachAgent - one
  1275. directly here, and the other from inside ShowRoomToMe.
  1276.  
  1277. "LeaveRoomStuff" is similar - it handles the stuff that needs to be
  1278. done when a character leaves the room he is in. The set of actions
  1279. done if similar to those done by EnterRoomStuff, except that the
  1280. ordering is reversed. "EnterRoom" puts the two together, moving a
  1281. player from one room to another. It also calls "DoPlayerEnterChecks"
  1282. after the player is in the new room. This is used for things like
  1283. triggering new random monsters in the Proving Grounds. After that,
  1284. there might be something funning happening on entering the new room,
  1285. so the enter checks for the room are called. If those checks indicate
  1286. that something special has happened, EnterRoom returns false, allowing
  1287. callers to not do further standard actions.
  1288.  
  1289. Putting it all together, "DoMove" is the top level routine which
  1290. attempts to move a player in a given direction from the player's
  1291. current room. First, it checks to see if the player has assumed a
  1292. position, such as sitting down, and prevents the move if so. Next it
  1293. checks to see if there is a connection from the current room in the
  1294. desired direction. If there isn't, then the move cannot happen. There
  1295. might be a special "nogo" string or action to handle that case. If the
  1296. direction exists but is locked, then the move is also prevented. Next,
  1297. the various checks associated with the attempted move are called.
  1298. DoPlayerLeaveChecks handles things like a player being blocked by a
  1299. monster in the Proving Grounds. The DoChecks call is for special
  1300. checks on that one direction, and DoRoomAnyLeaveChecks is for special
  1301. checks on any attempt to leave the current room. If all checks allow
  1302. further progress, EnterRoom is called to do the actual moving, and all
  1303. of the other actions, display updates, etc. that are needed.
  1304.  
  1305. The following two routines are used in this scenario to move machines
  1306. around. This is for the fixed machines like Packrat as well as the
  1307. random machines in the Proving Grounds. "TryToMove" checks to see if
  1308. the machine will be allowed to move in the indicated direction. If it
  1309. returns "true", then the machine can use "MachineMove" to move in that
  1310. direction. The things done for machines are very similar to those done
  1311. for players, except that there is no display associated with a
  1312. machine, so less updating need be done. Note, however, that a machine
  1313. moving into a room can bring light into that room, and thus the
  1314. displays of any players there might need updating.
  1315.  
  1316. Next are some routines for dealing with light sources. "AddLight" is
  1317. called whenever some activity is adding a light source to a room. This
  1318. is not used for agents doing this by moving into a room, but is used
  1319. for things like a player lighting a lamp in a dark room. The next
  1320. routine, "ActiveLightObject", uses AddLight, and is useful as an
  1321. action routine on an object that can be lit. Similarly, "RemoveLight"
  1322. and "ActiveUnLightObject" are useful for situations where a source of
  1323. light is being extinguished. A slightly different situation makes use
  1324. of "PassiveUnLightObject". In that situation, no agent action is
  1325. causing the light to go away - it is happening on its own. This is
  1326. useful for things like torches and lamps which can burn out.
  1327.  
  1328. Routine "CarryItem" is used when a player is trying to pick something
  1329. up. It is the code that prevents a player from carrying more than
  1330. MAX_CARRY objects. Note that objects have a property that indicates
  1331. who is carrying them. This is useful for when the object needs to
  1332. remove itself from its carryer. This is used in the squirrel quest in
  1333. the standard scenario to remove objects from the player that should
  1334. not be allows out into the rest of the scenario.
  1335.  
  1336. The next set of routines are related to allowing players to
  1337. interactively enter descriptions, the text of letters, etc. If the
  1338. scenario wants a single short line of input from the player, and that
  1339. player is using the full MUD client, then a text requester can be
  1340. used. For longer text input, however, the user should be able to use a
  1341. text editor where possible, and multi-line input otherwise.
  1342. "GetDocument" is the basic routine in the scenario used for that
  1343. purpose. "appendToDocument" is used as an input handler for a
  1344. character that is entering a document line-by-line. If it is passed a
  1345. string consisting only of a period (as entered by the player), then
  1346. the document is finished. The idle handler setup by GetDocument (to
  1347. handle the case of the player disconnecting while in the middle of
  1348. entering a document) is reset, the previous input handler is reset,
  1349. and the prompt is reset. Since something must happen when the document
  1350. is completed, appendToDocument assumes that an action has been set on
  1351. the player for that purpose. It retrieves and deletes the handler, and
  1352. then calls it. If the input string was not an end-of-document signal,
  1353. then it is simply concatentated onto the document being built up.
  1354.  
  1355. "docIdleAction" is the idle action used by GetDocument. It simply
  1356. saves a copy of the old idle action (since it will be deleted by the
  1357. call to appendToDocument), puts the saved, old copy of the document
  1358. into the current form of the document, and then calls appendToDocument
  1359. with a "." string, to indicate that the document is complete. After
  1360. that, it calls the saved previous idle handler, thus preserving the
  1361. proper stacking of such idle handlers.
  1362.  
  1363. If the player is using the full MUD client, or is using the SMUD
  1364. client locally, so that builtin "EditString" can work, then
  1365. GetDocument will use those facilities. "docEndAction" is the routine
  1366. which it sets up to be called when the user completes the string
  1367. editing. It is passed, by the system, the current value of the string
  1368. being edited, as well as an indication of whether or not the user
  1369. aborted out of the edit. Again, appendToDocument is called to complete
  1370. the editing.
  1371.  
  1372. Next comes "GetDocument" itself, the exported interface to this code.
  1373. It is passed a prompt, which is only used if line-by-line mode is
  1374. used. If the character can edit, and is not already editing something,
  1375. then the temporary properties on the player are setup, the idle action
  1376. is setup, and builtin EditString is called to allow the user to do the
  1377. actual editing. If the user cannot edit, then the input handler is set
  1378. to appendToDocument, the idle handler is set, and the routine returns
  1379. - control will then end up inside appendToDocument when the user
  1380. enters the next input line.
  1381.  
  1382. The next three routines are a variant of "GetDocument". They only
  1383. support line-by-line input, and they allow a caller-supplied routine
  1384. to handle each line of input from the user. This is used in the
  1385. building code when the user is entering an action.
  1386.  
  1387. Following the GetDocument routines are some routines which allow long
  1388. strings to be output with pagination. This is not needed when players
  1389. are using the full "MUD" client, be is quite useful for those that are
  1390. in text-only mode. The pagination done here is not very smart and will
  1391. output too many lines if the lines in the string to be printed are
  1392. longer than the output width in use. A better solution would perhaps
  1393. be to build some kind of pagination into the text-only clients (SMUD
  1394. and MUDAgent in text mode), handling tab expansion, proper word-
  1395. breaks, etc.
  1396.  
  1397. There are a number of important concepts that are used in this code.
  1398. The first thing defined is actually a thing, "paginateThing". This
  1399. will always contain one fixed property, "p_paginateParse", which is
  1400. assigned during the initial parsing and never changed again. This may
  1401. seem strange, but the purpose is to allow a function ("paginateParse")
  1402. to be used ("in paginateShowPage") before it is defined. This is
  1403. needed because both of those routines can reference the other. So,
  1404. since the AmigaMUD language has no direct method for doing "forward
  1405. references", this indirect method must be used.
  1406.  
  1407. Property "p_pPaginateSetup" is a flag which indicates whether or not
  1408. the complex parts of pagination had to be set up. This allows the
  1409. reset routine ("paginateReset") to know how much resetting it must do.
  1410. Properties "p_pPaginateString" and "p_pPaginateLen" describe the
  1411. string that is being paginated. They change as parts of the string are
  1412. printed. The remaining three properties are used to save the
  1413. character's prompt, input handler, and idle handler. This is done so
  1414. that we can restore them when we are finished displaying the string.
  1415.  
  1416. "paginateReset" puts things back to normal. It deletes all of the
  1417. properties that were added to the character, and sets the input and
  1418. idle handlers back to what they were before "Paginate" was called. If
  1419. parameter "goingIdle" is true, then paginateReset is being called as
  1420. part of the player leaving the game. In that case, we not only want to
  1421. reset our stuff on the character, but we also want to allow any other
  1422. "going idle" actions to happen. We do this by calling the saved idle
  1423. action as our last step. "paginateIdle" is the actual "idle action"
  1424. used here. It simply calls paginateReset with goingIdle 'true'.
  1425.  
  1426. Function "paginateShowPage" prints out one page of the string. This is
  1427. the routine that could be improved to split up long parts of the
  1428. string on word boundaries, but this would be quite expensive to do
  1429. using the AmigaMUD interpreter and strings. paginateShowPage first
  1430. obtains the values it needs to do its job. TextWidth(0) returns the
  1431. width in characters of the player's display, and TextHeight(0) returns
  1432. the height in text lines of the display. These are set automatically
  1433. if the "MUD" client is in use, and can be set by the player in the
  1434. standard scenario with the "width" and "height" commands. The while
  1435. loop continues outputting text until the desired number of lines have
  1436. been printed (by its best guess) or it runs out of text to print.
  1437. After the loop, if the string is empty, paginateReset is called to
  1438. reset the player, otherwise the remainder of the string is saved on
  1439. the player. If this is the first full page of the string that has been
  1440. printed, then the player's input prompt, input handler and idle action
  1441. are set to the ones defined here. These could have been set right away
  1442. in "Paginate", and unconditionally reset in paginateReset, but that
  1443. would result in extra "flapping around" of things like the prompt,
  1444. when the string is short enough to not need pagination.
  1445.  
  1446. "paginateParse" is the input handler used during pagination. It
  1447. accepts a "q" to end the display of the string, or an empty line (just
  1448. a carriage return) to display the next page. Note the use of "==" to
  1449. allow either a capital or lower-case "q". "Paginate" is the top-level
  1450. entry point for this code. It simply sets the string onto the
  1451. character and calls paginateShowPage.
  1452.  
  1453. Next in file "util.m" comes some routines, etc. dealing with stores.
  1454. The first such, "AddForSale", is used to add an item for sale in a
  1455. store. It is passed the room that is the store, the name of the item
  1456. (in the usual internal form), an optional description of the item, the
  1457. price of the item, and an optional routine to do special handling when
  1458. the item is purchased. A new thing is created to represent the item,
  1459. the various properties are attached, and the item is appended to the
  1460. list (created if needed) of items for sale in the store.
  1461.  
  1462. "AddObjectForSale" is similar, except that it adds for sale an already
  1463. existing object. This will be the case when the object is set up using
  1464. the build code. "SubOjbectForSale", also used from the build code, is
  1465. used to make an object be no longer for sale at the store. Note that
  1466. it removes properties "p_oPrice" and "p_oBuyChecker" from the object.
  1467.  
  1468. Routine "ShowForSale" is used as the action for the "shop" verb. It
  1469. simply shows the active client what is for sale in the current room.
  1470.  
  1471. "StoreBuy" is used as the main body for the "buy" verb. It takes a
  1472. string which is the internal-form name of the object to buy, and
  1473. attempts to buy one in the current location. The price is checked
  1474. against the player's current money. Note that if the player has flag
  1475. "p_pPrivileged" set, that player does not pay for purchases. If the
  1476. purchase is going ahead, a new thing is created which will be the new
  1477. copy of the item being purchases. It inherits most of its properties
  1478. from the model which was for sale. If there was a buy action set on
  1479. the model, then it is called, and it can cancel the sale, or can
  1480. complete it, in which case the standard message about buying is not
  1481. output. Note that other players can see when someone buys something,
  1482. although they are not told what was purchased.
  1483.  
  1484. "MakeStore" is used from the build code to make a room into a store.
  1485. This is done by attaching a p_rBuyAction to the room. The build code
  1486. uses the presence of that property to determine whether or not a room
  1487. is a store. "IsStore" makes that test. This is done as a routine,
  1488. since property "p_rBuyAction" is private to this source file.
  1489. Similarly, "UnmakeStore" removes the buy action, and any list of items
  1490. for sale.
  1491.  
  1492. The next pair of routines deal with special commands that are
  1493. available only at specific locations. Examples in this scenario are
  1494. the commands used in banks. The command is just a simple string, and
  1495. can include comma-separated alternative forms. Special commands are
  1496. checked for very early in processing user input. "AddSpecialCommand"
  1497. takes the room, the command and an action to be called to execute that
  1498. command. "RemoveSpecialCommand" takes the same three parameters. The
  1499. string and routine given to RemoveSpecialCommand must be exactly the
  1500. same as those given to AddSpecialCommand, else things will not work
  1501. out quite right.
  1502.  
  1503. "DoSay" is a simple routine which this scenario uses whenever someone
  1504. or some machine (excepting some simple special cases) wants to say
  1505. something out loud. It checks that the room allows normal speech,
  1506. echos the speech to the player if needed, and then simply calls the
  1507. builtin routine "Say" to do the speaking.
  1508.  
  1509. "checkAlias" is a subroutine of "parseInput", which is the input
  1510. handler this scenario uses for user input lines. Here is the sequence
  1511. of checks, etc. made on each input line:
  1512.  
  1513.     - if the line starts with '"', then use DoSay on the entire line
  1514.     - expand any alias in the command
  1515.     - if any object in the room, or carried by the active agent, has
  1516.     the first word of the input as its "act word", then call the
  1517.     corresponding action on that object
  1518.     - otherwise, check for a special command in this room, and if
  1519.     found, use it
  1520.     - otherwise, use builtin "Parse", with the standard grammar, to
  1521.     parse the input line
  1522.  
  1523. After parseInput comes the definition within the scenario of the codes
  1524. for the various special keys that the MUD client supports. Routine
  1525. "handleRawKey" is the normal handler for special key events. It uses
  1526. previously defined routine "InsertCommand" to echo and execute the
  1527. corresponding command.
  1528.  
  1529. "idleAction" is the standard idle action in the scenario. It removes
  1530. the icon of the leaving player from the displays of all other players
  1531. in the same room, removes light from the room if appropriate, and
  1532. resets things so that when the player reconnects, he will get a
  1533. display of the current location. idleAction then informs other players
  1534. in the room that the player has left the world.
  1535.  
  1536. "activeAction" in turn is the standard active action for this standard
  1537. scenario. It sends the players chosen text colours to the MUD client,
  1538. if needed, initializes the standard graphics if needed, and then calls
  1539. any other enter actions that other portions of the scenario may have
  1540. added. One of these is used by the build code to put up the "@"
  1541. button. Next, activeAction continues doing the reverse of idleAction,
  1542. by adding this player's icon to the screens of other players in the
  1543. room, and possibly bringing light into the room. Finally, it prints a
  1544. message saying that the player has entered the world.
  1545.  
  1546. The next set of routines implement the banks in the standard scenario.
  1547. The first three are special action routines called by parseInput. As
  1548. such, they have no parameters, but get what they need from the command
  1549. line tail, usually using builtin GetWord. "bankDeposit" wants a
  1550. number, and will try to deposit that number of blutos into the bank.
  1551. Note that each bank is separate - they all have a "p_rBankAccounts",
  1552. which is a list of the accounts at that bank. Each account consists of
  1553. a pointer to the thing of the account holder, and the number of blutos
  1554. in the account. bankDeposit will create an account for the player if
  1555. none exists. "bankWithdraw" is the reverse - it transfers blutos from
  1556. the bank account to the player. If the account is drained, it is
  1557. closed. "bankBalance" simply shows the player's current balance.
  1558.  
  1559. Routine "MakeBank" is publically exported, and is used to make a room
  1560. into a bank. It simply creates an empty account list, and uses routine
  1561. AddSpecialCommand (described earlier) to add the commands "deposit",
  1562. "withdraw" and "balance" to the room. Routine "IsBank", used from the
  1563. build code, tells whether or not a room is a bank. "UnmakeBank" tries
  1564. to make a room not be a bank. It returns a status value, with continue
  1565. indicating that the room was not a bank, fail indicating that it could
  1566. not unmake the bank because active accounts exist, and succeed
  1567. indicating that it was able to make the room not be a bank.
  1568.  
  1569. Next follow a couple of self-explanatory utility routines.
  1570.  
  1571. Routines "commonVerbTail", "VerbCarry" and "VerbHere" are worth close
  1572. examination. They are used to implement most of the common verbs in
  1573. the scenario. Each verb is essentially implemented with one or two
  1574. action properties, and a string property. If the action required for
  1575. the verb is just that of printing out a message, then the string
  1576. property will yield the message. Otherwise, the action property will
  1577. yield an action which is both a checker and a do-er for the verb. The
  1578. properties can exist on objects, rooms and agents, thus allowing a
  1579. fair amount of flexibility. VerbCarry and VerbHere are used as the
  1580. body of verb-handler routines for Verb1 verbs. VerbCarry looks for the
  1581. user-specified object in the player's inventory. VerbHere looks both
  1582. in the inventory and in the current room. Which one you use depends on
  1583. the normal English semantics of the verb you are adding.
  1584.  
  1585. commonVerbTail is the central code that is common to both VerbCarry
  1586. and VerbHere. Both VerbCarry and VerbHere handle the special case of
  1587. "all" being the user-specified object. VerbCarry will attempt to
  1588. operate on everything in the player's inventory. VerbHere will first
  1589. try all things in the inventory. If there are none, then it will try
  1590. to work on all in the current room. VerbHere will also check the
  1591. room's scenery for an object not found elsewhere, and will tell the
  1592. user that the operation cannot be done on it.
  1593.  
  1594. Let's examine a use and call with VerbHere in more detail:
  1595.  
  1596.     define t_base p_oUseString CreateStringProp().
  1597.     define t_base p_oUseChecker CreateActionProp().
  1598.     define t_base p_pUseChecker CreateActionProp().
  1599.  
  1600.     define tp_verbs proc v_use(string what)bool:
  1601.     VerbCarry("use", p_oUseString, p_oUseChecker, p_pUseChecker,
  1602.           "You cannot use", what)
  1603.     corp;
  1604.  
  1605.     Verb1(G, "use", 0, v_use).
  1606.     Synonym(G, "use", "apply").
  1607.  
  1608. Property "p_oUseString" is a constant string which can be attached to
  1609. an object or a room, and which will be the entire result of trying to
  1610. "use" something, if it is present. "p_oUseChecker" is an action that
  1611. can be attached to the object or room to do the required action, check
  1612. that it can or cannot be done, print special messages, etc. as
  1613. desired. "p_pUseChecker" serves the same purpose, but is only looked
  1614. for on the active agent. As the comment before "commonVerbTail" says,
  1615. separating this case out prevents some confusion relating to machines.
  1616. The verb routine "v_use" simply calls VerbCarry. It is a Verb1
  1617. routine, and has synonym "apply". This simple setup allows players to
  1618. try to "use" objects, with objects being able to have special actions
  1619. and checks, rooms being able to block or special-case using, and
  1620. special use-checks possibly attached directly to the player.
  1621.  
  1622. When VerbCarry is called, it is passed value 'what', which is the
  1623. internal form of the noun phrase that the player entered. So, the
  1624. first task of VerbCarry is to try to find the object that the user is
  1625. talking about. If 'what' is empty, then the user just typed the verb
  1626. by itself - VerbCarry sets variable "object" to nil and drops through
  1627. to a call to commonVerbTail. If 'what' is the string "all", then
  1628. VerbCarry will iterate through all items in the player's inventory,
  1629. calling commonVerbTail for each one, so long as it continues to return
  1630. 'true'. This is only done for objects which are not marked as
  1631. invisible, i.e. only for those which show up in the inventory. The
  1632. code handles the situation where the inventory list it is scanning is
  1633. changed during this process. Variable 'ok' is used to record the
  1634. success of the operations. If the player is not carrying any visible
  1635. object, then variable 'done' will not have been set, and so VerbCarry
  1636. will print an appropriate message, and its entire result will be
  1637. 'false', via 'ok'. For a normal call, builtin FindName will be used to
  1638. find a matching object in the inventory list. Such an object (or nil
  1639. from the empty 'what' case) is passed to the normal call to routine
  1640. commonVerbTail at the end of VerbCarry.
  1641.  
  1642. commonVerbTail has parameters as follows, most passed directly from
  1643. the caller of VerbCarry or VerbHere:
  1644.  
  1645.     direct - the property containing any direct string result
  1646.     indirect - the property containing any action to check for
  1647.     actorCheck - the property containing a check on the agent
  1648.     object - the object to work with, or nil
  1649.     failHeader - what to print if the operation fails
  1650.     verbName - the common name of the verb, used in messages
  1651.     name - the formatted name of the object, if any
  1652.  
  1653. The order of the checks that commonVerbTail does may not be the best,
  1654. but so far I haven't been able to come up with a better one. First,
  1655. the player (or machine) is checked for an 'actorCheck' routine. If one
  1656. is found, the object is set as "it", and the routine is called. The
  1657. routine must return a status value, with the values having the usual
  1658. interpretation:
  1659.  
  1660.     fail - the operation has failed - no more processing
  1661.     succeed - the operation has succeeded - no more processing
  1662.     continue - neither result - continue processing
  1663.  
  1664. If there is no 'actorCheck' on the agent, or if it returned continue,
  1665. then a checker on the current room is looked for. It is called in the
  1666. same manner, with the same effect. Next, a simple string is checked
  1667. for in the room, but only if the object is nil or there is no direct
  1668. string on the object. Such a string is simply printed out, and
  1669. processing is allowed to continue. Next, a checker on the object (if
  1670. any) is looked for and called as usual. Finally, if all is still well
  1671. (or, more likely, nothing has happened yet), a direct string is looked
  1672. for on the object, and printed if found. Note that if a direct string
  1673. is printed in either case, a newline is automatically added.
  1674.  
  1675. The final result of commonVerbTail, and hence of VerbCarry or
  1676. VerbHere, depends on what was done during processing. This has been
  1677. maintained in variable 'st', which will have the result from the last
  1678. indirect routine call, if any. If that status is not 'fail', then the
  1679. entire action is considered to have succeeded. If there were no
  1680. checkers on the actor or room that were triggered, and no object was
  1681. given (the player entered no noun-phrase), then a standard usage
  1682. message is printed. Otherwise, nothing appropriate was found, so that
  1683. the verb is not applicable to the object, a message to that effect is
  1684. printed, and commonVerbTail returns 'true'.
  1685.  
  1686. The above simple run-through of this code is unlikely to have given
  1687. the reader a true understanding of what is going on here, much less
  1688. why things are done that way. Only experience in working with the
  1689. scenario can give you that kind of understanding. Also, note that
  1690. these routines were not coded this way immediately - several revisions
  1691. were needed until the current form appeared to be satisfactory. This
  1692. is true of much of the general tools in the scenario - they were not
  1693. designed from first principles, but have evolved over a couple of
  1694. years of working within the scenario. There are many other ways that
  1695. similar results can be obtained, and the practiced (or brave!)
  1696. programmer should feel free to investigate alternatives. If you find
  1697. any that work out well, let me know.
  1698.  
  1699. Following this family of routines are a couple more handy utilities.
  1700. "ResetObjects" is used in places like the squirrel quest. It runs
  1701. through a passed list of objects, and takes each one from where it is
  1702. and puts it back to where it is supposed to be. This latter is
  1703. represented by the property "p_oHome". Note the use of property
  1704. "p_oWhere", which is maintained by the scenario to keep track of where
  1705. the various objects are.
  1706.  
  1707. "RemoveAllFromInventory" is used in similar circumstances. It removes
  1708. all occurrences of a given object ('what') from the inventory of agent
  1709. 'who'. It does this using recursive routine "scanList", so that
  1710. occurrences inside containers will be found and removed.
  1711.  
  1712. We now finally come to the first location in the scenario. This is the
  1713. arrivals room. In the full standard scenario, this room is part of the
  1714. mini-mall, but since the mini-mall is not created until much later,
  1715. the arrivals room is simply an isolated room at this point. It doesn't
  1716. even have any graphics associated with it yet. It is defined this
  1717. early since it is needed when a player is initialized, since that
  1718. initialization includes moving the new player to the arrivals room.
  1719. Previously discussed routine SetupRoom is used to do the setup.
  1720.  
  1721. Following the definition of the arrivals room is the setup of the
  1722. SysAdmin character. This is done separately here so that that
  1723. character can be set up a bit different from the normal "nondescript
  1724. adventurer". In particular, SysAdmin is made privileged (doesn't have
  1725. to pay for purchases), is given 10000 blutos and is given a special
  1726. description. Property "p_pEnterActions" is setup on SysAdmin right
  1727. away, so that code which sets itself up to add scenario-entry routines
  1728. to players can do so immediately to SysAdmin.
  1729.  
  1730. The final routine in file "util.m" is "newPlayer". This is the routine
  1731. which is made the "new player action" for the scenario. It will be
  1732. executed automatically on behalf of a player when that player first
  1733. enters the game. We have seen many of the properties being set up here
  1734. discussed in this file, as well as most of the routines being called.
  1735. I hope that the reader can understand what is going on in this routine
  1736. without further explanation.
  1737.  
  1738.  
  1739. Basics - verbs.m
  1740.  
  1741.  
  1742. File "verbs.m" contains the definitions of many of the more important
  1743. verbs in the scenario. Most verbs can simply use the common verb code
  1744. defined "util.m", but some of the more important ones require special
  1745. processing, or do not use that code for some other reason.
  1746.  
  1747. The first thing done in "verbs.m" is to use the "Word" builtin to
  1748. define a few separator words that are used in a number of verbs. These
  1749. are done here so that they can be readily found, instead of doing them
  1750. before the first verb that happens to use them.
  1751.  
  1752. The first real verbs defined are the movement verbs. These are
  1753. implemented using the DoMove routine defined in "util.m", and most
  1754. have several synonyms, including very short abbreviations. Verb "go",
  1755. and its abbreviations simply allows the player to put a movement verb
  1756. in front of the direction to move.
  1757.  
  1758. Verb "with", defined next, is a somewhat special one. It is defined in
  1759. the scenario, but it cannot be used. It is intended for use with
  1760. magical objects, which add a whole set of new commands to the world.
  1761. That set of commands is represented as a grammar attached to the
  1762. object. The tail of the user's command, after the "with" and the noun
  1763. phrase for the object, is parsed according to the grammar retrieved
  1764. from the object. For example, if a wizard creates a "staff of power",
  1765. which supports additional commands "zap", "thwack" and "jump", a
  1766. player carrying the staff could enter:
  1767.  
  1768.     with the staff of power do zap blue dragon
  1769.  
  1770. The English isn't terribly good here, but the structure works. The
  1771. difference between setting "magical spells" up this way as opposed to
  1772. using the "cast" command, is that here the spell is associated with an
  1773. object, rather than with a character. That way, a non-wizard can
  1774. execute the spells attached to a magical object. A small "with toy"
  1775. was used to test the "with" command, but its definition is commented
  1776. out.
  1777.  
  1778. After the "with" verb comes a set of verbs that allow players to sit
  1779. on benches, lie on beds, etc. There really isn't much semantics
  1780. attached to these actions, but they can give more feeling of reality
  1781. to social interactions in a MUD.
  1782.  
  1783. Following these verbs, starting with routine "lookAtObject", is the
  1784. code for the "look" verb. "lookAtObject" is just a little utility
  1785. which will do the job of printing out an object's description. This
  1786. includes calling a possible description checker, and getting the
  1787. actual description from either a direct string or an indirect routine.
  1788. Note the use of builtin "NPrint" to avoid some possible spoofing on
  1789. this important activity.
  1790.  
  1791. Actual verb routine "v_look" uses "lookAtObject" to look at "all"
  1792. objects in the current room. LookAtCharacter, defined in "util.m" is
  1793. used to look at characters. "look" allows looking in a specific
  1794. direction, which shows the room's direction description for that
  1795. direction. "look" allows examination of objects that are either being
  1796. carried by the character, or are in the current room. The room's
  1797. scenery is checked if no matching object is found. Verb "look around"
  1798. is a special case of "look" - it simply shows the current room to the
  1799. character. Similarly, verb "exits" just shows the obvious exits in the
  1800. current room. This is useful if the player is in "superterse" mode.
  1801.  
  1802. After "look" comes "read", which is set up very similarly. Verb
  1803. "inventory" simply shows the player what his character is carrying,
  1804. and how many blutos it currently has.
  1805.  
  1806. Next follows code to implement the "get" verb. I will describe this
  1807. verb in more detail. "DoGet" is an exported routine which handles the
  1808. basics of getting an object. It is used by the "get" verb in a couple
  1809. of places. "p_oNotGetString" is used in DoGet as a special string to
  1810. print when something is not gettable. It can be set directly, or by
  1811. using the build code. DoGet is passed the room the object is in, the
  1812. character who is picking the object up, and the object itself. It
  1813. returns 'continue' if the operation proceeds normally, i.e. nothing
  1814. special happens during the getting. It returns 'suceed' if the getting
  1815. is reported as successful, but special things may have happened. It
  1816. returns 'fail' if something went wrong and the object was not gotten.
  1817.  
  1818. First, DoGet creates string "whatName", which is the external form of
  1819. the name of the object. This is used when printing messages about the
  1820. object. Recall that when a verb routine is called, it can be passed
  1821. the internal form of the name of the object to operate on. If the
  1822. object has been set up to be not gettable by its creator, then DoGet
  1823. will print a message saying so, and return 'fail'. Otherwise, it will
  1824. look for a "p_oGetChecker" routine on the object. If there is such a
  1825. checker, it will be called, and its status result interpreted in the
  1826. normal way: continue -> nothing special, continue processing; fail ->
  1827. do not continue trying to get the object; succeed -> the getting was
  1828. successful, but it has already been completed. The object is passed to
  1829. the checker routine, but for convenience is also set up as "It". If
  1830. there was no object checker, or the checker says to continue, DoGet
  1831. next calls any "get checkers" in the room. These checkers can be used
  1832. to control the picking up of objects in the room, as limited by some
  1833. aspect of the room itself. If those checkers are absent or allow the
  1834. getting to continue, DoGet calls "CarryItem", defined in "util.m" to
  1835. see if the character has the capacity to carry the item. If so, the
  1836. item will have been added to the character's inventory. So, DoGet
  1837. needs only to delete the item from the room's contents list, remove
  1838. property "p_oWhere" from the item, and possibly inform others in the
  1839. room about the action that has just been taken. Note the special check
  1840. and message for a hidden wizard getting an object.
  1841.  
  1842. "getAllStub" uses DoGet to have the active character get an object
  1843. from the current room. DoGet is more general. The form of getAllStub
  1844. is such that it can be passed to "DoAll".
  1845.  
  1846. "v_get" is the actual verb routine for the verb "get". It is a Verb1
  1847. routine, indicating that it only has a direct object (or a list of
  1848. them, automatically handled by the AmigaMUD parsing code). Thus, it
  1849. has a single string parameter, which is the internal form of the name
  1850. of the object, and returns a bool value, indicating whether or not
  1851. further actions from the input line should be processed. v_get first
  1852. checks for an empty "what" value, which can occur if the player just
  1853. enters something like "get.". Next, special checking is done to allow
  1854. the form "take inventory" to be the same as using the "inventory"
  1855. verb. Similarly "take exit" is the same as "exit", "take entrance" is
  1856. the same as "enter" and "get up" is the same as "stand up". "get all"
  1857. is handled by using DoAll with getAllStub. Recall that DoAll returns
  1858. fail if it cannot work on anything in the passed list.
  1859.  
  1860. The standard handling code follows. First, an external form of the
  1861. object name is produced by FormatName. Next, the object is looked for
  1862. in the room's contents. If a unique match is found, then the object
  1863. is identified, and DoGet is called to try to pick it up. If FindName
  1864. indicates that the user's input form was ambiguous, a message to that
  1865. effect is printed. Otherwise, no matching name was found in the room's
  1866. contents list. So, a check is made to see if there is an agent by that
  1867. name in the room. Characters cannot pick up other characters! If there
  1868. isn't, the a check is made to see if the character is already carrying
  1869. something that matches the name. (Forgetting having already picked
  1870. something up is a common mistake.) If not, then the name is checked
  1871. for in the room's scenery string. If a match is found there, then the
  1872. player is told that it cannot be picked up, else the player is told
  1873. that there is no matching object to be found. Note that much of the
  1874. code and checking is done in order to be "user friendly".
  1875.  
  1876. Following the get routines come similar routines for dropping things:
  1877. "DoDrop", "dropAllStub" and "v_drop". There are slight differences
  1878. from the getting, but otherwise the above explanation applies.
  1879.  
  1880. "give" is the first verb we have seen here that takes both a direct
  1881. and an indirect object. Thus it is a "Verb2" verb, and "v_give" takes
  1882. two string parameters. v_give is a fairly long routine. The first
  1883. interesting part is that of the three checkers that can be called. The
  1884. first is the "p_oUnGetChecker" on the item. This can be used to make
  1885. an item that cannot be dropped (the same checker is called by DoDrop)
  1886. or given away (like the cursed things in Hack). Next, any "p_pGivePre"
  1887. on the receiving character is called. A wizard can use one of these to
  1888. prevent people from giving them things. Some machines in the scenario
  1889. also use them. If that is well, then a "p_oGiveChecker" on the object
  1890. itself is checked. This allows for things that cannot be given away,
  1891. such as the pears in the standard scenario. Following that, the
  1892. receiving character's "p_pGivePost" is called to do any checks for the
  1893. character, after any checks by the object itself. If all checks allow
  1894. continuation, then the object is transferred from the active character
  1895. to the receiving character, and appropriate messages are printed. Note
  1896. that if you give away something that you caused to be created (e.g. by
  1897. buying it in a store), the "p_oCreator" property is changed to be that
  1898. of the receiving character - you are truly giving the object away, not
  1899. just lending it to someone. If no object matching the object name
  1900. given by the user is found, v_get checks to see if it is a string that
  1901. ends in "bluto", or something similar. If it is, the code looks for a
  1902. valid count of blutos, and will try to transfer that many blutos from
  1903. the active character to the receiving character. No special checks
  1904. were implemented for this kind of transfer.
  1905.  
  1906. Next come verbs "put in" and "take from", which are used to put things
  1907. into containers, and to take things from containers. They are similar
  1908. in nature to "v_give", but with less checkers, and no special case for
  1909. blutos. Note that the container is looked for both in the character's
  1910. inventory and in the room's contents, but the object is looked for
  1911. only in the character's inventory. "v_lookin" implements the verb
  1912. "look in", which allows players to look inside containers. Similar
  1913. verbs "fill", "lock" and "unlock" follow.
  1914.  
  1915. Following these fancy verbs comes a set of much simpler utility verbs.
  1916. "quit" simply calls builtin "Quit", which arranges for the player to
  1917. be disconnected when the current input line has been processed.
  1918. Several fairly uninteresting verbs follow. The next interesting ones
  1919. are those dealing with command aliases. "showAliases" is a utility
  1920. routine which simply shows the current aliases a character has. Note
  1921. how these aliases are stored - a list of things is attached to the
  1922. character's thing. Each of those things contains two properties.
  1923. p_sAliasKey is the word that is being aliased, and "p_sAliasValue" is
  1924. the expanded value of that alias. Command "aliases" simply calls
  1925. showAliases to print out the current aliases. Command "alias" will
  1926. also do so if it has no further arguments. If is given just an alias
  1927. word, then any alias for that word is removed. Otherwise an alias for
  1928. the word is added or changed to whatever follows in the command.
  1929.  
  1930. More boring but useful commands follow. Note command "words", which
  1931. uses builtin ShowWords to show all of the command words in the
  1932. standard grammar the scenario uses. There are quite a few. Command
  1933. "shop" directly uses "ShowForSale", defined in "util.m". Next follows
  1934. a whole bunch of stuff which uses the standard verb code in "util.m"
  1935. to implement a set of common verbs. Routines "GetVerbStringProp" and
  1936. "GetVerbCheckerProp" are used to allow the build code to work with
  1937. these verbs. These are followed by more boring verbs. Note the verbs
  1938. "typo", "bug" and "gripe", which allow players to log complaints about
  1939. the scenario. Verb "cast" is worth mentioning. It allows a wizard or
  1940. apprentice to call up any action defined in their set of in-use
  1941. tables. The routine called this way must have no parameters and must
  1942. return no result, but it can get further words from the rest of the
  1943. command line, using GetWord, GetTail, etc. I have a separate source
  1944. file which contains a number of "spells" that I find useful when being
  1945. an active wizard in the game. Other wizards are encouraged to do the
  1946. same. A few more boring verbs finish off the file.
  1947.  
  1948.  
  1949. Basics - chat.m
  1950.  
  1951.  
  1952. File "chat.m" contains a number of commands and routines that are used
  1953. only for communication between players and for poses, which are
  1954. actions the players can do that are seen by other players, but which
  1955. have no actual effect on the scenario. They are used only for the
  1956. effect they have on the minds of the other players.
  1957.  
  1958. The "chat" command allows the player to enter into "chat mode". In
  1959. this mode, input lines are simply spoken out loud, instead of being
  1960. passed to the normal parser. This is done by having a handler routine,
  1961. "chatHandler", which is assigned as the character's input handler.
  1962. This routine supports its own set of aliases, and has escapes to allow
  1963. normal commands to be given while in chat mode.
  1964.  
  1965. Verb "whisper" accepts a few common input formats for separating the
  1966. name of the agent to be whispered to from the words to be whispered.
  1967. Verb "v_pose" handles the "pose" verb, which can also be abbreviated
  1968. as ":", as is done in other MUD systems.
  1969.  
  1970. Next follows two sets of routines and verbs that are specific pose-
  1971. like verbs. SysAdmin's are encouraged to add others as they see fit.
  1972. These specific poses are divided into two categories - those that
  1973. normally must be seen by others, and those that can be heard by
  1974. others. This distinction is used when the player is in a dark room, or
  1975. is hidden.
  1976.  
  1977.  
  1978. basics - quests.m
  1979.  
  1980.  
  1981. File "quests.m" contains the code which implements the general
  1982. framework for quests in the scenario. This includes the "Questor"
  1983. machine and his office. There are three types of quests that this code
  1984. supports:
  1985.  
  1986.     - direct - the scenario calls a routine when it determines that a
  1987.     quest has been solved
  1988.     - give - the player must give something to Questor
  1989.     - tell - the player must speak a special word to Questor
  1990.  
  1991. All quests have a name, which shows up on the sign outside of
  1992. Questor's office. They also have a description routine and a hint
  1993. routine. These are routines so that the resulting strings can be
  1994. dependent on the player, or something else. This is used for the
  1995. "Whatzit" quest in the standard scenario. All quests also have a
  1996. checker routine, which is used to uniquely identify the quest. Direct
  1997. quests do not actually use this checker, other that for identifying
  1998. the quest if a player tries to accomplish it more than once.
  1999.  
  2000. In "give" quests, the item being given will already be set up as "It".
  2001. The checker must simply determine if that item is one that must be
  2002. given to Questor to satisfy the quest. This can be done based on the
  2003. name of the item, it's ancestry, or its thing. The latter will
  2004. restrict the use of the quest, since no-one can solve the quest if
  2005. someone else (or a machine!) is carrying the special object. The
  2006. checker returns a status value, with values indicating:
  2007.  
  2008.     continue - the item does not solve the quest
  2009.     succeed - the item solves the quest
  2010.     fail - the item appears to be an attempt to cheat on the quest -
  2011.     it should be destroyed (this is done by "questorPre")
  2012.  
  2013. The checker for a "tell" quest is passed the string of a word spoken
  2014. by the player. The checker returns a boolean value, with 'true'
  2015. indicating that the word is the magic one to solve the quest. If you
  2016. are building a "tell" quest (like the "squirrel" quest), then it is a
  2017. good idea to make the magic word change from player to player (it can
  2018. be stored on the player for testing), so that the solution to the
  2019. quest doesn't become common knowledge.
  2020.  
  2021. Routines exported from this file are:
  2022.  
  2023.     QuestDirect - define a direct quest
  2024.     QuestGive - define a "give" quest
  2025.     GiveToQuestor - utility which is useful inside your "give" checker
  2026.     to indicate that someone is giving something to questor.
  2027.     QuestTell - define a "tell" quest
  2028.     DoQuest - called by the scenario to indicate that the conditions
  2029.     for a direct quest have been met. It will check whether or not
  2030.     the character has already completed the quest.
  2031.     DoneQuest - returns 'true' if the active agent has already done
  2032.     the indicated quest.
  2033.     ShowQuests - prints an indication of how many quests the passed
  2034.     character has solved.
  2035.     SetupQuestorOffice - allows Questor's Office to be linked at the
  2036.     specified location.
  2037.  
  2038. This file also defines the "quests" verb, which show how many quests
  2039. someone has completed.
  2040.  
  2041. This is the last file included by source file "basics.m". They are the
  2042. set which define the basic operation of the scenario. Those wishing to
  2043. create an entirely new scenario can start with just this set, and
  2044. start defining locations next. The standard scenario next sources file
  2045. "combat.m", which defines the simple combat and monsters supported in
  2046. the Proving Grounds area. If those writing a new scenario want that
  2047. type of combat included, they should source it as well.
  2048.  
  2049.  
  2050. Combat - fight.m
  2051.  
  2052.  
  2053. File "fight.m" contains the code which defines combat in the scenario.
  2054. This includes utilities for use in monsters, weapons and armour; code
  2055. to create armouries and healer shops, etc. Also contained here are the
  2056. routines for performing the actual combat, from the verb "hit" all the
  2057. way to the killing of monsters and players. First, some constants are
  2058. defined:
  2059.  
  2060.     STEPS_PER_REGAINED_HIT_POINT - this is the number of movements
  2061.     that a character makes in order to regain one hit point.
  2062.  
  2063.     BLUTOS_AFTER_DYING - this is the number of blutos a player is
  2064.     given when the player is killed and moved to the arrivals
  2065.     room.
  2066.  
  2067.     RANDOM_MONSTER_LIFE - the is the number of steps that a typical
  2068.     random monster will take before it disappears.
  2069.  
  2070.     FOREVER_LIFE - this value, when used as a monster life value
  2071.     (property p_mMovesUntilVanish), indicates that the monster
  2072.     should not vanish on its own.
  2073.  
  2074. Next come several new properties, that can be attached to characters,
  2075. monsters, objects and rooms. These will be seen in the code, and are
  2076. each commented with their basic purpose.
  2077.  
  2078. The first routine in this file is "fighterDesc". It is attached to
  2079. characters which are fighters, and can return additional description
  2080. which is seen when the character is looked at. It basically combines
  2081. the character's weapon, shield and armour into a proper descriptive
  2082. sentence.
  2083.  
  2084. Routine "useItem" can be attached to objects which are weapons,
  2085. shields or armour, as an action routine. It makes sure that the
  2086. character has a "p_pDescMore" list, and makes sure that "fighterDesc"
  2087. is in that list. It also modifies the character's active status based
  2088. on any bonuses given by the object being used (or worn or wielded).
  2089. Routine "unUseItem" is the reverse - it removes any special bonuses
  2090. associated with an item. Routines "armourDrop", "shieldDrop" and
  2091. "weaponDrop" are suitable for use as "p_oUnGetChecker" routines on
  2092. armour, shields and weapons. They ensure that any benefits associated
  2093. with the objects are lost when the object is discarded. Routines
  2094. "armourWear", "shieldUse" and "weaponUse" do the reverse - they set
  2095. the active item to be inuse by the active character. Any previously
  2096. inuse item of the same type is unused first. These routines are used
  2097. as verb routines, so they print confirmation messages to the player.
  2098.  
  2099. "SetupWeapon" is the first routine exported from this source file. It
  2100. is used to set up an object as weapon, shield or armour. It attaches
  2101. the relevant bonuses and values to the thing which is the model for
  2102. the item, and also attaches any needed action routines from the set
  2103. just defined. "WeaponSell" combines the functions of "AddForSale"
  2104. (from file "util.m") with SetupWeapon, to create and add for sale a
  2105. new item as weapon, shield or armour. A single call to this routine
  2106. containing the name, description and statistics for the item is enough
  2107. to create it and make it for sale at a store. Similarly, routines
  2108. "healBuy" and public routine "HealSell" are sufficient to setup an
  2109. amount of healing for sale in a store. Note that "healBuy" randomizes
  2110. the amount of healing that is actually done.
  2111.  
  2112. "showStats" prints out the status of the passed character, and is used
  2113. as the basis for the "status" command defined later.
  2114.  
  2115. Public routine "AddExperience" is used to add a given amount of
  2116. experience (which can be negative) to the given character. The routine
  2117. takes care of adjusting the character's level if needed, and of adding
  2118. or removing strength or speed. It will also notify the character of
  2119. any changes made. Note that AddExperience can be called by any agent,
  2120. and will change and inform the correct one. This allows it to be used
  2121. in situations such as one player attacking and killing another.
  2122. Routine "FindLoot" adds a given quantity of blutos to the current
  2123. character, and informs the character of that.
  2124.  
  2125. Routine "KillPlayer" is called when a player or machine kills another
  2126. player. It takes care of a lot of the details involved. First, it
  2127. calls any routines that have requested notification of the death of
  2128. this character. This is used by the tracker spiders to stop tracking
  2129. someone who has died. Next, all of the victim's possessions are
  2130. dropped in the current location, using routine "DoDrop" from file
  2131. "verbs.m", which will take care of things like destroying pears, etc.
  2132. Next, the victim is penalized for dying. The victim is then forced to
  2133. be unhidden, and is floated off to the arrivals room. Appropriate
  2134. messages are show to all involved, and icon updating is handled.
  2135. Finally, if the victim was the only source of light in a dark room
  2136. (such as in many locations in the Proving Grounds), then light is
  2137. removed from that location. Nothing is done to the arrivals room -
  2138. that room is assumed to be not dark.
  2139.  
  2140. "MonsterHitPlayer" allows a monster to attack a player. Whether or not
  2141. the monster can and does hit are first determined. Next, the amount of
  2142. damage is determined, taking into account the attacker's damage level,
  2143. the victim's armour level, and the victim's strength. If the victim is
  2144. killed by the attack, then KillPlayer is called to handle that. The
  2145. attacker, the victim, and any bystanders are notified as appropriate.
  2146. This routine returns 'true' if the victim is still alive, and can hit
  2147. back.
  2148.  
  2149. "KillMonster" is called when a character kills a monster. Note that
  2150. this can be a non-player character such as Packrat as well as a player
  2151. character. Appropriate messages are printed, and the victim's
  2152. possessions are dropped. The winner receives loot if the victim had
  2153. any money. The victim's icon is removed from the displays of anyone in
  2154. the room.
  2155.  
  2156. "PlayerHitMonster" is called to allow a player character to hit a
  2157. monster. The monster's "p_mMovesUntilVanish" is reset to the default
  2158. value, the player's current target is set to this monster, and the
  2159. monster's target may be set to be the attacking player. Messages are
  2160. printed to others in the room. Next, PlayerHitMonster determines if
  2161. the player manages to hit the monster, and if so, how much damage is
  2162. done. Damage done is awarded to the player as experience using
  2163. "AddExperience", described earlier. If the monster is killed, the code
  2164. checks for a special "p_mKillAction" on it. If one is found, it is
  2165. called, else KillMonster is called, and the machine representing the
  2166. monster is destroyed using builtin DestroyMachine.
  2167.  
  2168. Routine "PlayerHitPlayer" is similar to both MonsterHitPlayer and
  2169. PlayerHitMonster. The three routines should be rewritten to share
  2170. common code for their calculations and message printing.
  2171.  
  2172. Random monsters in rooms are generated according to a pair of lists
  2173. attached (usually by inheritance) to the rooms. "p_rMonsterList" is a
  2174. list of pointers to the models of the monsters. "p_rMonsterChance" is
  2175. an array of numbers which is used to calculate whether or not any
  2176. monster is to appear, and if so, which kind. "InitMonsterModels" sets
  2177. these values up, ready for the adding of monsters. Routine
  2178. "AddPossibleMonster" adds a given monster to the set. The value given
  2179. to InitMonsterModels represents the proportion of the time that no
  2180. monster will be selected. The value given to AddPossibleMonster
  2181. represents the proportion of the time that that monster will be
  2182. selected for generation.
  2183.  
  2184. The next three routines are used with monsters which run away from
  2185. other monsters. See the comments there. They are used with the deer in
  2186. the standard scenario. Had you noticed that the deer run away from
  2187. wolves and trolls?
  2188.  
  2189. Each monster can have an associated set of "actions" that it can do on
  2190. random occasions. These are simply strings that are shown to others in
  2191. the room. "MonsterAction" selects one and displays it.
  2192.  
  2193. Routine "PickNewTarget", along with "huntTarget" is used to allow a
  2194. monster to try to select a new target when it enters a room. This is
  2195. only needed if the monster doesn't currently have a target.
  2196.  
  2197. Public routine "MonsterMove" is the normal movement routine attached
  2198. to these monsters. It attempts to move in the indicated direction. If
  2199. the move is successful, any "p_mArrivedAction" is checked for and
  2200. called. Similarly, each agent in the room is informed of the arrival
  2201. of this monster. These checks allow deer to immediately leave rooms
  2202. with enemies in them, and to leave rooms when an enemy arrives. Next,
  2203. if the monster's current target is in the current room, the monster
  2204. will attack the target if the room is lit, otherwise a small message
  2205. is sent to everyone in the room. If the target is not present, there
  2206. is a one in five chance that the monster will try to pick another
  2207. target from the characters in the new room.
  2208.  
  2209. Routine "DoMonsterMove" can be used as part of the the basic step
  2210. routine for one of these monsters. It can choose to attack the current
  2211. target, or look for another one; or it can attempt to move, doing a
  2212. random "action" if the move fails.
  2213.  
  2214. "MonsterStillMoving" asks if the monster is still active. If the
  2215. monster is not still active, the passed "dieAction", if any, will be
  2216. called. Following that, messages indicating that the monster has left
  2217. are displayed, and the monster's icon is removed. Then, if the
  2218. monster's target is targetting the monster, that link is removed. The
  2219. monster's inventory is then destroyed. Note that no attempt is made to
  2220. drop any of the items - the monster is assumed to have carried them
  2221. off to wherever it went. Thus, in this scenario, it is not a good idea
  2222. to have single, unique items that are needed for quests, since they
  2223. may end up in the possession of a monster which can carry them off and
  2224. destroy them if no-one manages to kill the monster. Alternatively make
  2225. sure that the object's cannot fall into the hands of a monster. This
  2226. is what is done with the Troll-King's shovel. Finally, the machine
  2227. representing the monster is destroyed. If the monster is still active,
  2228. it's time-to-vanishing is decremented.
  2229.  
  2230. Routine "MonsterReschedule" simply schedules a call to the monster's
  2231. move action (often "MonsterStillMoving") at a random future time
  2232. depending on the monster's speed. Monsters with higher speed move and
  2233. do things more often. "DummyMonsterInit" can be used as the init
  2234. routine passed to builtin CreateMachine when a machine for a monster
  2235. is created. It will simply call MonsterReschedule to start the monster
  2236. up. "MonsterInit" can be used when a monster appears in a way that
  2237. might be visible to others - it prints a message and shows the
  2238. monster's icon if the room is lit.
  2239.  
  2240. Starting with routine "MonsterNoNo" is code and definitions to set up
  2241. a generic monster model which most other monsters inherit from.
  2242. MonsterNoNo is used as a checker for many actions that a player might
  2243. try to do to a monster. The resulting generic monster, called
  2244. "GenericMonster", is exported for use elsewhere.
  2245.  
  2246. "CreateMonsterModel" is used to simplify the creation of a new type of
  2247. monster. It is passed the various strings, actions and numbers which
  2248. define the monster, and does all of the work to create the new monster
  2249. from them. It returns the thing representing the new monster. No
  2250. machine is actually created for the monster, however - that is only
  2251. done when an actual example of this monster is needed.
  2252.  
  2253. Recall from previous description that a monster can have a number of
  2254. "actions" which will be printed out on occasion. "AddModelAction" adds
  2255. such an action to a monster. The passed string is simply something of
  2256. the form "roars" or "hops around" - when it is printed out by the
  2257. standard monster code, it will be preceeded by the name of the
  2258. monster. "CreateMonster" uses a model monster and creates an actual
  2259. example of that type. The creator of the monster is made the current
  2260. target of the monster. The monster's hit points are set to a random
  2261. value up to the maximum obtained from the model. An actual machine is
  2262. created for the new monster, and all agents in the room who wish to be
  2263. notified of the creation of a new monster will be so notified. This
  2264. latter is used to make the deer run away if a wolf or troll is
  2265. created. "CreateSpecificMonster" is similar, except that the passed
  2266. model is used exactly as the parent of the new monster - nothing is
  2267. varied.
  2268.  
  2269. "PickNewMonster" examines the set of random monsters available in the
  2270. given location, and "throws the dice" to see if one should be created.
  2271. If one is to be created, it is picked according to the liklihoods
  2272. established using AddPossibleMonster. CreateMonster is used to create
  2273. the new monster, so it will have a random amount of hit points.
  2274.  
  2275. Function "monsterEnterCheck" is the trigger for the monsters. It is
  2276. attached to players when they enter the Proving Grounds, and executes
  2277. every time a character moves. It does incremental healing of the
  2278. player, and will create a random monster if the player does not have a
  2279. current target and the room is not marked as "safe". Similar checker
  2280. "monsterLeaveCheck" allows a monster to take a final swing at a player
  2281. who is trying to run away, or to block the player. Note that this
  2282. routine returns 'fail' if the monster's last hit kills the player,
  2283. thus preventing the player from doing the move in the arrivals room.
  2284.  
  2285. Routine "StandardAttack" is called to have a player attack a monster
  2286. or another player in a given location. When attacking a monster, the
  2287. relative speeds of the player and the monster randomly govern which
  2288. will hit first. If either is hit first and is killed, they cannot hit
  2289. back.
  2290.  
  2291. "InitFighter" sets a character up to use the combat stuff defined
  2292. here. In the standard scenario, this is done when the player enters
  2293. the Proving Grounds area, but it could also be done when a player is
  2294. first initialized, if everyone is to be setup for combat.
  2295.  
  2296. "LeaveFighting" is used as an exit checker when leaving the Proving
  2297. Grounds - it prevents monsters from leaving and carrying their combat
  2298. outside the Proving Grounds.
  2299.  
  2300. "HealingBuy" can be attached to a room as the function to execute when
  2301. someone tries to buy something in that room. It checks to see if the
  2302. active character needs any healing, and complains if not. Otherwise,
  2303. it uses "StoreBuy" from file "util.m" to actually do the purchasing.
  2304.  
  2305. This file adds six commands to the main grammer. The first is command
  2306. "status", abbreviated as "st". This is normally used by a player to
  2307. examine the combat status of their character. It can also be used on
  2308. other characters in the room by wizards. Verb "wield" is used to try
  2309. to wield a weapon. Shields can be used by pre-existing verb "use", and
  2310. armour is put on using pre-existing verb "wear". The basic combat verb
  2311. is "hit" ("fight", "attack", "kill", "h", "k"). It searches for a
  2312. player or machine in the current room to be the target of the attack.
  2313. If a matching player is found, "PlayerHitPlayer" is used to do the
  2314. attack. If a monster is found, then it is checked for a
  2315. "p_mFightAction" property. If one is found, it is used to handle the
  2316. attack attempt, otherwise "StandardAttack" is used.
  2317.  
  2318. The remaining three verbs simply control the amount of output that is
  2319. generated by the combat code. Reducing the amount is handy for those
  2320. connecting a low speeds, or for those who don't want their screen
  2321. cluttered up with output that doesn't give much information.
  2322.  
  2323.  
  2324. Combat - monsters.m
  2325.  
  2326.  
  2327. Source file "monsters.m" contains the definitions and actions for most
  2328. of the monsters that are seen in the Proving Grounds. It should be
  2329. fairly easy for others to add more. A couple of the machines seen in
  2330. the Proving Grounds are not exactly "monsters", and are defined
  2331. elsewhere.
  2332.  
  2333. Routine "RandomMove" uses the code from "fight.m" to create the actual
  2334. "step" routine used by most of the random monsters. It checks if the
  2335. monster is still moving (hasn't timed out), and if so, it looks to see
  2336. if there is a "p_mSpecialAction" attached to the monster. If so, that
  2337. action is called, and if it returns 'false', indicating that it has
  2338. not done all things necessary to keep the monster active, or killed
  2339. off the monster, RandomMove decrements the monster's active time, and
  2340. calls MonsterReschedule to setup the monster's next step. If there is
  2341. no special action attached, "DoMonsterMove", described above, is used
  2342. as the basic step action.
  2343.  
  2344. "MonsterPickUp" provides for some interesting, but rarely seen
  2345. activity. It is normally only attached to "humanoid" monsters, like
  2346. the goblins. With this action attached, the goblins will attempt to
  2347. pick up things that they encounter. They will try to wear armour,
  2348. use shields or wield weapons, if they are better than any they already
  2349. have. This can make it difficult to recover especially good objects
  2350. lost in the deeps. Such objects are also subject to being destroyed if
  2351. the monster carrying them times out and goes away.
  2352.  
  2353. The next half dozen routines are used to implement "tracker" monsters.
  2354. The only example of these in the standard scenario is the tracker
  2355. spiders which guard the spider egg. Such monsters attach a move
  2356. checker to the player who they are tracking, and it records the moves
  2357. the player makes. At their own speed, the tracker follows that
  2358. recorded path, thus "tracking" the victim. If the victim is
  2359. encountered, the path is reset. "TrackerDieNotify" is attached to the
  2360. victim so that it is called if the victim is killed. It simply clears
  2361. and removes the record of anything tracking that victim.
  2362. "TrackerChecker" is the actual routine which records the movements. It
  2363. will delete the tracking information if all of the trackers are gone
  2364. (e.g. if the player kills all the tracker spiders). It also does the
  2365. slight optimization of cancelling a move that is the reverse of the
  2366. previously recorded one. "TrackerDie" is the companion to routine
  2367. TrackerDieNotify. It deletes tracking information from a target if the
  2368. tracking monster goes away. "TrackerKill" is an explicit routine
  2369. called when a tracker monster is killed. Among them, these routines
  2370. should be sufficient to keep unneeded tracker and track lists from
  2371. accumulating. "TrackerInit" is the init routine that should be used
  2372. when a tracker monster is created. It initializes both the monster and
  2373. the player to allow the tracking to occur. "TrackerMove" is the basic
  2374. step routine for a tracker monster. If the tracker finds the target,
  2375. it uses the usual "MonsterHitPlayer" to attack the target, and resets
  2376. the track record. If the target is not found, the tracker tries to
  2377. move in the next direction recorded in the track. If the track runs
  2378. out without the monster finding the player, or some move following
  2379. the track fails (e.g. trying to go through a locked door), the tracker
  2380. falls back to the normal random-monster method of moving.
  2381.  
  2382. Following the tracker code, the standard monsters are created. The
  2383. only thing to take note of here are the routines "deerArrivalCheck",
  2384. "deerCreationCheck" and "deerArrivedCheck", which are the special
  2385. checker routines that allow the deer to run away from wolves and
  2386. trolls.
  2387.  
  2388.  
  2389. Town - mall.m
  2390.  
  2391.  
  2392. Source file "mall.m" defines the mini-mall area. A number of special
  2393. constructs and situations occur in this area, so the reader is
  2394. encouraged to browse the code to see where to find examples of how to
  2395. do things. None of these will be explained in detail.
  2396.  
  2397. The entrance corridor is a good example of how to set up a lot of
  2398. detail. Note the room drop checker in the garbage room, which attempts
  2399. to destroy objects dropped by their creator. The smell checker used
  2400. here shows how such a checker can be added and then removed from a
  2401. player.
  2402.  
  2403. The Beauty Shop shows some complexity. When the players enter there,
  2404. three additional mouse-buttons are added to the screen. These trigger
  2405. either the use of GetDocument to get a new player description, or of
  2406. the use of the icon editor to edit either the player's icon or the
  2407. player's cursor. Special care was taken with this code to allow it to
  2408. operate even if the player leaves the Beauty Shop in the middle of
  2409. using it. Similar code cleans things up if the player leaves the game
  2410. in the middle of doing these things.
  2411.  
  2412. The camera object in the store has quite a bit of code associated with
  2413. it. This includes a new verb, "photograph"/"photo"/"snap". This allows
  2414. the player to take photographs of characters, objects, rooms and
  2415. directions. The code allows for description strings and actions, and
  2416. will record anything written on an object. This code, and similar code
  2417. for the mirror in the squirrel quest, is the reason why the
  2418. description routines all return their strings, rather than just
  2419. printing them.
  2420.  
  2421. Routine "checkForGift" and its associated properties is an example of
  2422. how to make sure that something only happens once per player. The
  2423. player is recorded on a list so that this minor event does not need to
  2424. add a property to the player, which would be using up one of the
  2425. possibly scarce property slots available.
  2426.  
  2427.  
  2428. Town - streets.m
  2429.  
  2430.  
  2431. This file creates the town and its surroundings, including the streets
  2432. and sidewalks, the park, the ring road, and the area with the pear
  2433. tree. The "pear quest" is defined here. Note that a fair amount of
  2434. code was required to make sure that there aren't any ways to "cheat"
  2435. on the pear quest. Are there any cheats that I missed?
  2436.  
  2437.  
  2438. Town - squirrel.m
  2439.  
  2440.  
  2441. File "squirrel.m" defines the squirrel quest and the areas around it.
  2442. Setting up an interesting quest is not an easy task. The first step is
  2443. perhaps the hardest - deciding exactly what the quest is, and setting
  2444. it up so that it is neither too hard nor too easy. Solving the
  2445. squirrel quest is made easier with the standard MUD client program, so
  2446. that input history can be used to help place all the tape.
  2447.  
  2448. The squirrel is a machine which has a fairly complex movement
  2449. algorithm. She must interact with the structure of her tree, with
  2450. where the player is, and with where there are pieces of tape. She must
  2451. move "intelligently" enough to avoid get trapped too easily, but there
  2452. must be a way to trap her. It took a couple of revisions of her code
  2453. to get the difficulty to what I wanted. The first revision made it far
  2454. too easy to trap her, so the ability to jump from branch to branch was
  2455. added, thus leading to the need for the isolated, broken-off branch.
  2456. Care has been taken to make sure that the descriptive output produced
  2457. is correct and natural English. Wizards wishing to create "tell"
  2458. quests may want to take note of the code in "squirrelStep" which
  2459. creates a random "word" as the quest solution.
  2460.  
  2461. Also of interest in this source file are the various special objects
  2462. that can be used or created. E.g. using the hoe out-of-doors creates a
  2463. weed, which is again slightly special. E.g. the mirror in the bedroom
  2464. shows the character his own description. Things are arranged so that
  2465. taking a picture of the mirror will capture a picture of the player.
  2466. There is room for two people on the bed, but unless SysAdmin "poofs"
  2467. in, there can only ever be one person on it.
  2468.  
  2469.  
  2470. Town - machines.m
  2471.  
  2472.  
  2473. This file defines the two non-player characters that wander around the
  2474. town (Packrat and Caretaker) as well as the "enter-exit machine",
  2475. which records the people passing through the mini-mall entrance
  2476. passage. The enter-exit machine isn't a machine in the technical
  2477. AmigaMUD sense, but it is the first machine-like thing I coded in
  2478. AmigaMUD, so I don't want to change it much or remove it, for
  2479. sentimental reasons.
  2480.  
  2481. Caretaker doesn't do much. He just wanders around, picking things up.
  2482. He will drop them in their "home" location if he can. Many objects
  2483. with no reasonable "home" are marked with the lost and found room as
  2484. their home, so they will end up there, if Caretaker finds them and
  2485. then finds his way there. The only thing he will do for players is to
  2486. drop things he is carrying, if they tell him to. He will also accept
  2487. anything given to him.
  2488.  
  2489. Packrat is a bit more complex. She wanders around picking things up
  2490. and dropping them, but she also has special checks for being in the
  2491. field with the pear tree. She will pick a pear and then eat it.
  2492. Packrat's main feature is that she will do whatever you tell her to
  2493. do, sometimes with fascinating results. One player tells me that he
  2494. used her as a companion in the Proving Grounds, attacking the same
  2495. monsters he was attacking. Since she can kill them, she gained
  2496. experience, levels, etc. just like a player character would. She can
  2497. also be made to solve quests, although some wouldn't work because of
  2498. restrictions on how some things operate. Packrat:
  2499.  
  2500.     - echoes any pose/emote she sees
  2501.     - greets whoever greets her
  2502.     - repeats out loud any whisper she overhears
  2503.     - accepts and thanks you for anything given to her
  2504.     - is involved in one of the quests
  2505.  
  2506.  
  2507. Town - mail.m
  2508.  
  2509.  
  2510. This file adds the in-mud mail system, the postman, and the bulletin
  2511. board facility to the scenario. Mail is done by buying a pen and a pad
  2512. of paper in the store; writing letters to someone; and posting them in
  2513. a mailbox. Mailman, a non-player character, follows a route around the
  2514. streets of the town, taking letters out of the mailboxes and taking
  2515. them to the mailroom in the mini-mall. The letters are left there,
  2516. where they can be picked up by the character they are addressed to. An
  2517. example of the bulletin boards (which also use pen and paper) can be
  2518. found on the north side of the west street in the town.
  2519.  
  2520. Verbs "write"/"mail" and "post" are created in this source file. The
  2521. only routine publically exported is "MakeBulletinBoard", which will
  2522. create a bulletin board in the room it is passed. When writing a
  2523. letter to someone, a check is made to see if that someone has
  2524. registered for mail in the mailroom. This is done so that it is less
  2525. likely that a mail sender thinks that someone should have received
  2526. mail when that someone hasn't even discovered the mail system.
  2527.  
  2528.  
  2529. The Proving Grounds - proving0.m - proving6.m
  2530.  
  2531.  
  2532. The source for the Proving Grounds is split up as follows:
  2533.  
  2534.     proving0.m - the surface level
  2535.     proving1.m - the sewers level
  2536.     proving2.m - the deep sewer level
  2537.     proving3.m - the caves level
  2538.     proving4.m - the chasm area
  2539.     proving5.m - the doors room area
  2540.     proving6.m - the 3-D maze area
  2541.  
  2542. Again, I will only be mentioning a few things from these source files.
  2543.  
  2544.  
  2545. proving0.m - the surface
  2546.  
  2547.  
  2548. The "monsterSet" routines generate a set of random monsters for the
  2549. room they are given. The set is normally inherited from a general
  2550. model of the room, rather than being attached separately to each room.
  2551. The birds, the drinking troll, and later the drinking goblin, are
  2552. slight variations of the standard monsters.
  2553.  
  2554. The checkers associated with entering and exiting the Proving Grounds
  2555. area handle the initialization of the combat values on characters,
  2556. and keeping monsters out of the non-combat areas of the world.
  2557.  
  2558. A number of useful weapons, armour, etc. are offered for sale at the
  2559. six stores in the Proving Grounds. They make use of various routines
  2560. provided earlier in the scenario. The high price on the flashing
  2561. sword, the Hammer of Thor, and the enchanted shield are due to their
  2562. bonus attributes, which are given to the player when they are used.
  2563. This is done via their various "bonus" properties. The oil lamp and
  2564. the torch use "DoAfter", implemented in "base.m" to control their
  2565. burning interval. Note how the state of the lamp, oilcan and torch are
  2566. set only on the model objects. When the timed actions happen, they
  2567. subtract one from the "p_oState" of the individual objects, which is
  2568. first inherited from the model, updated on the individual, and then
  2569. fetched from the individual.
  2570.  
  2571. The apple tree and the apples are essentially a copy of the pear tree
  2572. and pears from "streets.m", with the addition of extra things to the
  2573. "appleEat" routine. The iron bar grating by the steam is implemented
  2574. in a fashion quite similar to that of the metal plate in the mini-mall
  2575. entrance passage.
  2576.  
  2577.  
  2578. proving1.m - the sewers level
  2579.  
  2580.  
  2581. The pair of drainage grates use what are by now fairly standard stuff
  2582. to operate. Note the check in "grateLift2" that tries to keep the
  2583. "non-intelligent" monsters from passing. The idea is that things like
  2584. rats and snakes should not be able to open the grate and pass through,
  2585. but gremlins and goblins should. Quite deliberately, tracker spiders
  2586. cannot - I didn't want them getting out to the surface.
  2587.  
  2588.  
  2589. proving2.m - the deep sewers level
  2590.  
  2591.  
  2592. The dagger quest is fairly simple, but it does try to avoid giving
  2593. more than one dagger to someone. It isn't too hard to trick it,
  2594. however. When my character is strong enough to venture down here, I
  2595. often run around among the goblin chamber, the chamber with the rats,
  2596. and the snake pit, bashing the critters to get my experience up. A bit
  2597. later, I start including the goblin maze in my "tour". Did you notice
  2598. the possible free leather armour in the snakepit? Did you also notice
  2599. that the stuff sold in the goblin armoury is better than that sold on
  2600. the surface? The goods offered by the goblin shaman are the only real
  2601. "magic" in this scenario, outside of wizards directly manipulating
  2602. things.
  2603.  
  2604.  
  2605. proving3.m - the caves level
  2606.  
  2607.  
  2608. The monsters are tougher down here. This level has the egg quest,
  2609. which is done very similar to the dagger quest. It also has stuff that
  2610. is needed to solve the heart quest. Did you find a key? Did you notice
  2611. your weapon become less effective? Good heavens! I had completely
  2612. forgotten that I gave away a goblin sword! Other than these things,
  2613. this file is just a large bunch of rooms.
  2614.  
  2615.  
  2616. proving4.m - the chasm area
  2617.  
  2618.  
  2619. The chimney is infinitely deep - perhaps later some more stuff will be
  2620. added further down. I want to provide access to the bottom of the
  2621. chasm at some point. Did you find out how to get past the guardian
  2622. troll without defeating him, which is pretty difficult? Going after
  2623. the set of large trolls without sufficient protection is a good way to
  2624. get your character killed!
  2625.  
  2626.  
  2627. proving5.m - the doors room area
  2628.  
  2629.  
  2630. There is a lot of code in this file, but not very many locations. That
  2631. is likely to be an unfortunately common situation - implementing
  2632. something interesting, and doing it properly, takes a lot of code,
  2633. much of which most players won't notice, and perhaps won't ever
  2634. trigger. The first 130 lines simply define the door that must be
  2635. unlocked with the small key. Following that are the simple rooms of
  2636. the corridor, followed by the effects code to draw the doors room.
  2637. There are two views of the doors room ("drawDoors2" and "drawDoors3"),
  2638. one for when the player has not passed through the doors and cannot
  2639. see what is on the other side, and one for when the player has passed
  2640. through a door and is shown the entire room. Next come some utility
  2641. routines to help in defining the locations in the doors room, followed
  2642. by uses of them to create those locations.
  2643.  
  2644. The first interesting code starts with routine "door1Desc", which is
  2645. the description routine for the doors. The door in question will be
  2646. set into 'It' before this routine is called. There should not be any
  2647. way to examine the front of a door when the door is open, but, just in
  2648. case a wizard messes around, the code here will return an appropriate
  2649. description. Similarly, the winch is described by "winchDesc", which
  2650. returns a string dependent on the current position of the winch, as
  2651. stored in general-purpose field "p_oState".
  2652.  
  2653. In this scenario, an object is only in one room's contents list at a
  2654. time (not absolutely necessary, if the object cannot be taken). So,
  2655. there is a pair of objects for each door in the doors room. Property
  2656. "p_oOtherDoor" points to the other one of such a pair. Property
  2657. "p_oSound" is the individual sound that a door makes. "p_oDoorName" is
  2658. the name of the door, in terms of where it is in the room.
  2659. "p_oSoundList" is a property attached to "o_winch", which is simply
  2660. used to hold the list of rooms that can see and hear what is happening
  2661. to the doors. The doors room area is considered to be one large room
  2662. for the purposes of these sounds.
  2663.  
  2664. "o_doorModel2" is the model for the back of the doors - they can be
  2665. opened and closed easily by their handles. Routine "door2Checker"
  2666. simple forces people to explicitly open the doors, rather than just
  2667. walking through the closed doors. "door1Checker" denies all access
  2668. through the doors from the front when the door is closed. Note that
  2669. when coming back out of a door, "door2Checker" sounds the
  2670. characteristic sound of that door. The individual doors themselves are
  2671. then created.
  2672.  
  2673. "p_oDoors" is attached to o_winch as a list of the five doors. List
  2674. "p_oMapping" is a list of five ints which is a mapping of the order in
  2675. the which the doors will fall this time. This order is determined
  2676. randomly when the winch is turned ("turnWinch").
  2677.  
  2678. Routine "doorsEntranceDesc" provides a description of the doors room.
  2679. It works hard to generate decent English output to describe the state
  2680. of all five doors. After this routine, the rooms of the doors room (on
  2681. both sides of the doors) are added to the list of rooms which can hear
  2682. the door sounds. Routine "doorDrop", along with others, uses this
  2683. routine, and other code, to tell everyone concerned about a door
  2684. dropping closed. Someone standing beside the winch will just hear the
  2685. winch unwinding. Routine "doorsDrop" is the trigger routine, which
  2686. causes the "next" door to drop. It can be triggered by time or by the
  2687. player walking along the corridor. "clearWinch" uses "doorDrop" in a
  2688. loop to let the winch quicky unwind all the way. Note the way both of
  2689. these routines index indirectly through "p_oMapping" to get the doors
  2690. in the correct order. "doorsEnter" is an enter checker used on the
  2691. rooms in the corridor to call "doorsDrop" as the player moves.
  2692.  
  2693. "doorsLightEnter" is an entry checker for all of the rooms in the
  2694. front part of the doors room. It cancels the time-driven closing of
  2695. the doors and either closes the next door, or closes all of the doors,
  2696. depending on whether or not the player has brought light into the
  2697. doors room.
  2698.  
  2699. Routine "turnWinch" is the turn action for the winch. It sets the
  2700. winch to fully cranked, picks the order in which the doors will fall
  2701. this time, announces the doors rising to anyone who is in the doors
  2702. room with a source of light. Of course, this source of light causes
  2703. them all to fall immediately! This is an example of something needed
  2704. in a MUD that would not be needed in a one-player adventure game. If
  2705. there is no-one to see the doors open, then the line reading
  2706.  
  2707.     DoAfter(DOOR_TIME, o_winch, doorsDrop);
  2708.  
  2709. starts up the timed events which will close the doors automatically.
  2710. The doors close by themselves every DOOR_TIME (8) seconds.
  2711.  
  2712.  
  2713. proving6.m - the 3-D maze area
  2714.  
  2715.  
  2716. The 3-D maze in this scenario is the most complex code in the
  2717. scenario, requiring almost 1000 lines of code for what is essentially
  2718. just one room. Users, however, can think of there being 10 x 10 x 16
  2719. or 1600 rooms, some of which are impassible. In terms of "thing"s,
  2720. there really is only one room - the graphics and description are
  2721. dependent on the X, Y and Z position of the character in the maze, as
  2722. recorded on the character. I won't describe the code here in too much
  2723. detail, but I will point out some features.
  2724.  
  2725. Routine "mazeDropCheck" is quite important - because of the way these
  2726. "rooms" are implemented, we cannot let the player try to drop things
  2727. in them. Because all of the maze is the same room, any checks for
  2728. special actions (e.g. "drawBridgeDesc") must check the co-ordinates of
  2729. the character to see what they should say. "mazeIdle" is used to move
  2730. the player out of the maze, and drop the shovel, when the player exits
  2731. the game. This is to allow other players to try the maze. Similarly,
  2732. "leaveShovel" takes away the shovel when the player leaves the area.
  2733.  
  2734. Routine "maze" is used a lot. It is given the X, Y and Z co-ordinates
  2735. of a point within the maze, and returns the maze code of that position
  2736. in the maze. This is just faking out a 3-dimensional array of ints in
  2737. a language that doesn't have multi-dimensional arrays. "markSpace" and
  2738. "dumpSpaces" are part of some elaborate code to produce nice English
  2739. output describing the open spaces around a given position. "showMaze"
  2740. shows and describes the player's current view in the maze. It
  2741. describes the current position, describes the direction the tugging is
  2742. coming from, and uses specially coded "ray-tracing" to draw the area
  2743. around the player that is visible.
  2744.  
  2745. Routine "mazeMove" attempts to move the player in the specified
  2746. direction in the maze, describing what is happening. Note the 'while'
  2747. loop for falling through open space until something solid is hit.
  2748.  
  2749. Code starting with routine "makeRow" is used to define the shape of
  2750. the maze. This is done layer by layer, using strings to define each
  2751. row of the layer. For those who solved this quest: did you notice my
  2752. initials in the maze? Did you find the two ways back to the front of
  2753. the maze from the back of it?
  2754.  
  2755.  
  2756. The Builder Code - build1.m, build2.m, bguild.m
  2757.  
  2758.  
  2759. I won't describe much of the code in these files - most wizards will
  2760. not be trying to produce code like this. "build1.m" contains the code
  2761. which handles the textual build commands. A few routines in it are
  2762. also used by the button-building code, which is in file "build2.m". A
  2763. lot of the code and definitions in "build1.m" relate to routine
  2764. "bv_actionLineHandler", which is an input-line checker passed to
  2765. "GetCheckedDescription". It parses and checks the lines it is given,
  2766. and uses them to add on to a piece of AmigaMUD code being built up in
  2767. string "p_pActiveCode". This code is then dynamically compiled by
  2768. passing it to builtin "StringToAction" to produce various kinds of
  2769. checker and action routines.
  2770.  
  2771. Note the use of three new grammars to handle the build command sets. A
  2772. lot of the code which implements the various build commands is quite
  2773. straightforward - there is just a lot of it. "bv_poof" will be of
  2774. interest to wizards who want to magically teleport characters around.
  2775.  
  2776. There isn't much to say about "build2.m". Most of the handling for
  2777. things just goes through a sequence of input/button handlers that
  2778. varies from sequence to sequence. "AutoRedraw" is used when something
  2779. is done that affects the image of the current room. Sometimes, such as
  2780. when "poofHandler" calls "bv_poof", a routine in "build1.m" can be
  2781. used to do all or most of the work required to handle a button click.
  2782.  
  2783. Routine "makeBuilder" is a "spell" routine. It is intended to be used
  2784. from normal command mode by a wizard who has it in his private symbol
  2785. table. This is done using the "cast" command, as in:
  2786.  
  2787.     cast makebuilder Fred
  2788.  
  2789. Spell "unmakebuilder" is similar.
  2790.  
  2791. File "bguild.m" defines the Builder's Guild area of the scenario. It
  2792. is quite standard except for the books that it creates with long text
  2793. in them, and for the way it sets up the Playpen. Of some interest is
  2794. the code starting with "scanList", by which the playpen code attempts
  2795. to remove all playpen-created objects from characters when they leave
  2796. the playpen area. The last code in the file sets up the "Whatzit"
  2797. quest. Where this is defined should be a pretty big hint on how to
  2798. solve that quest!
  2799.  
  2800.  
  2801. The files included by "usenet.m" are:
  2802.  
  2803.     news.m - the simple usenet news reader/poster
  2804.     email.m - the simple usenet email reader/sender
  2805.  
  2806. I will not describe the code in these files. The only thing of note
  2807. about it is the complexities of presenting a prompt-response interface
  2808. to the user, and the use of the "System" builtin to trigger external
  2809. AmigaDOS commands on the server machine, in order to post news and
  2810. send email.
  2811.  
  2812. There is one thing that some sysadmins may want to control. This is
  2813. whether or not "uuxqt" is automatically run after sending mail. I use
  2814. this to force sending of local mail to the recipient. If you do not
  2815. want this, change or comment out the line in "email.m" reading:
  2816.  
  2817.     CharacterThing(Character("SysAdmin"))@p_pRunUUXQT := true.
  2818.  
  2819.  
  2820. Extras
  2821.  
  2822. There are some extra files included in the scenario source archive, in
  2823. the "Extras" directory:
  2824.  
  2825.     buildtest.so - a file that can be sourced by a builder to do the
  2826.     testing of building as in the documentation
  2827.  
  2828.     Frog.m - a wizard-mode source file as seen in the documentation
  2829.  
  2830.     hex.m - output integers in hex and binary
  2831.  
  2832.     numbers.m - output integers in American English words
  2833.  
  2834.     icons.d - Draco program I used to build the icon values
  2835.  
  2836.     temple.cg - an IFF image of a "temple area". This shows how bad an
  2837.     artist I am! The intent here is that the character cursor
  2838.     could move around the squares in the image, and go through
  2839.     various doors. To see this image loaded into MUD, copy it to
  2840.     AmigaMUD:images, then go into wizard mode and enter:
  2841.  
  2842.         GShowImage(nil,"temple.cg",0,0,320,100,0,0).
  2843.  
  2844.  
  2845.     spells.m - a wizard-mode source file that defines a bunch of
  2846.     useful spells. These can be accessed with the "cast" command.
  2847.     Note that the spells are only defined for the wizard who
  2848.     sources the file.
  2849.  
  2850.     wanderers.m - a wizard-mode source file that allows the creation
  2851.     of a set of 26 "wanderers". These are machines whose purpose
  2852.     is to act somewhat like human players, and stress-test the
  2853.     system. On a database that you do not want to keep, you can
  2854.     source this file, then move to somewhere handy (like a corner
  2855.     of the street) and enter:
  2856.  
  2857.         createWanderers().
  2858.  
  2859.     Then stand back! Note that the wanderers are capable of
  2860.     combat, and are pretty good at it, and hard to hit. I've had
  2861.     the full set running for four days now on my A4000T, and all
  2862.     is still well. They soon get into the Proving Grounds, and
  2863.     leave monsters running all over. For some fun, let them run
  2864.     for a day or so, then build a protected fighter and go kill
  2865.     some of them. I got over 50,000 blutos from one that way! Once
  2866.     started, there is no easy way to shut down the wanderers (I've
  2867.     never tried), so don't create them in a database that you care
  2868.     about - they interfere too much with normal players, and put
  2869.     too much load on the server for good response time.
  2870.